3

I want to define matchers to check if a parser can accept a string or not. I did it but not feeling good.

Dart unittest code:

library test_parser;

import 'package:unittest/unittest.dart';
import '../lib/shark_parser.dart';

main() {
  SharkParser parser;
  setUp(() {
    parser = new SharkParser();
  });
  tearDown(() {
    parser = null;
  });

  group("paramType parser", () {
    test("should accept valid types", () {
      expect(parser.paramType(), accept("String"));
      expect(parser.paramType(), accept("int"));
      expect(parser.paramType(), accept("List"));
      expect(parser.paramType(), accept("List<User>"));
    });
    test("should not accept invalid types", () {
      expect(parser.paramType(), notAccept("#"));
      expect(parser.paramType(), notAccept("0"));
      expect(parser.paramType(), notAccept("String()"));
      expect(parser.paramType(), notAccept("List< User >"));
    });
  });
}

Custom matchers:

Matcher accept(String matchingString) => new AcceptMatcher(matchingString);

Matcher notAccept(String matchingString) => new NotAcceptMatcher(matchingString);

class NotAcceptMatcher extends Matcher {
  String matchingString;

  NotAcceptMatcher(this.matchingString);

  bool matches(item, Map matchState) {
    return !item.end().accept(matchingString);
  }

  Description describe(Description description) {
    return description.add("parser not accept string: $matchingString");
  }

  Description describeMismatch(item, Description mismatchDescription,
                               Map matchState, bool verbose) {
    mismatchDescription.add("accepts it");
    return mismatchDescription;
  }
}

class AcceptMatcher extends Matcher {

  String matchingString;

  AcceptMatcher(this.matchingString);

  bool matches(item, Map matchState) {
    return item.end().accept(matchingString);
  }

  Description describe(Description description) {
    return description.add("parser accept string: $matchingString");
  }

  Description describeMismatch(item, Description mismatchDescription,
                               Map matchState, bool verbose) {
    mismatchDescription.add("doesn't accept");
    return mismatchDescription;
  }

}

You can see I have to define two matchers NotAcceptMatcher and AcceptMatcher. The logic is pretty similar but I don't know how to make it simple.

Is there any other simpler solution to do it?

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
Freewind
  • 193,756
  • 157
  • 432
  • 708

1 Answers1

3

You can use the isNot matcher to invert your accept matcher:

expect(parser.paramType(), isNot(accept("#")));

It reads a little funny, so you can create a function to do it:

Matcher notAccept(String s) => isNot(accept(s));

expect(parser.paramType(), notAccept("#"));

The description might not read perfectly, but it saves you some work.

Matchers might seem verbose, but if you want you can make your matcher definition a little more concise by shedding some type annotations and using short variable names. I find this an ok tradeoff when I'm writing a class or function to be called by a framework, because I'm not worried about documenting it for other users.

class AcceptMatcher extends Matcher {
  final acceptString;

  AcceptMatcher(this.acceptString);

  matches(item, _) => item.end().accept(acceptString);

  describe(d) => d.add("parser accept string: $acceptString");

  describeMismatch(item, d, _, __) => d..add("doesn't accept");
}

Another option is to just use the predicate matcher:

Matcher accept(String s) => 
    predicate((i) => i.end().accept(s), "parser accepts $s");
Justin Fagnani
  • 10,483
  • 2
  • 27
  • 37