为unittest定义“accept”和“notAccept”匹配器的更好方法

时间:2014-02-16 03:13:50

标签: dart matcher dart-unittest

我想定义匹配器以检查解析器是否可以接受字符串。我做到了,但感觉不舒服。

Dart unittest代码:

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 >"));
    });
  });
}

自定义匹配器:

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;
  }

}

您可以看到我必须定义两个匹配器NotAcceptMatcherAcceptMatcher。逻辑很相似,但我不知道如何简单。

还有其他更简单的解决方案吗?

1 个答案:

答案 0 :(得分:2)

您可以使用isNot匹配器反转accept匹配器:

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

它看起来有点搞笑,所以你可以创建一个函数来执行它:

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

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

描述可能无法完美阅读,但它可以为您节省一些工作。

Matchers可能看起来很冗长,但如果你想要,你可以通过删除一些类型注释和使用短变量名来使你的matcher定义更简洁一些。当我编写一个由框架调用的类或函数时,我发现这是一个很好的权衡,因为我并不担心为其他用户记录它。

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");
}

另一种选择是使用predicate匹配器:

Matcher accept(String s) => 
    predicate((i) => i.end().accept(s), "parser accepts $s");