我有一个相当复杂的antlr4语法,它利用了访问者模式。我想测试访客的某些部位。测试个人访问规则的好方法是什么?
我的访问者有很多这样的规则,我想测试一下:
@Override
public Object visitQux(ExclParser.QuxContext ctx) {
return visitChildren(ctx);
}
我的测试代码基本如下:
PrintStream ps = new PrintStream(stdError, false /* autoFlush */, "UTF-8")
ANTLRInputStream input = new ANTLRInputStream(is);
MyLexer lexer = new MyLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
MyParser parser = new MyParser(tokens);
parser.removeErrorListeners();
MyErrorListener errorListener = new MyErrorListener(ps, filename);
parser.addErrorListener(errorListener);
MyVisitor visitor = new MyVisitor();
visitor.setParser(filename, parser, errorListener);
ParseTree tree = parser.qux(); // <--- This is the line I want to vary.
Object result = visitor.visit(tree);
assertThat(describeExpectation(), result, equalTo(this.expectedOutput));
理想情况下,我将能够使用参数化测试测试任何访问者。但是为了获得我想访问的解析树(parser.qux),我不能在表中指定qux()的任何变体,因为parser.qux()不是静态的。
任何想法吗?
我创建了一个关于如何测试antlr访问者的示例项目(使用mockitotestng)。完整的源代码可以在GitHub上找到。以下是最重要的部分:
public class MyVisitor extends DemoBaseVisitor<String> {
@Override
public String visitPlus(final DemoParser.PlusContext ctx) {
return visit(ctx.left) + " PLUS " + visit(ctx.right);
}
@Override
public String visitLiteralNumber(final DemoParser.LiteralNumberContext ctx) {
return ctx.getText();
}
}
任何我对访问者的测试:
public class MyVisitorTest {
private final MyVisitor myVisitor = new MyVisitor();
@Test
public void visitPlus_joinsOperatorsWithWordPLUSAsSeparator() throws Exception {
// setup
final DemoParser.PlusContext plusNode = mock(DemoParser.PlusContext.class);
plusNode.left = mockForVisitorResult(DemoParser.ExpressionContext.class, "2");
plusNode.right = mockForVisitorResult(DemoParser.ExpressionContext.class, "4");
// execution
final String actual = myVisitor.visitPlus(plusNode);
// evaluation
assertEquals(actual, "2 PLUS 4");
}
private<T extends RuleContext> T mockForVisitorResult(final Class<T> nodeType, final String visitResult) {
final T mock = mock(nodeType);
when(mock.accept(myVisitor)).thenReturn(visitResult);
return mock;
}
@Test
public void visitLiteralNumber_returnsTextValueOfNumber() throws Exception {
// setup
final DemoParser.LiteralNumberContext literalNumberNode = mock(DemoParser.LiteralNumberContext.class);
when(literalNumberNode.getText()).thenReturn("42");
// execution
final String actual = myVisitor.visitLiteralNumber(literalNumberNode);
// evaluation
assertEquals(actual, "42");
}
}
在您的特殊示例中,您有一个调用visitChildren()的方法,您将测试调用该方法返回访问所有子节点的聚合结果(聚合是什么取决于您对aggregateResult
方法的实现)。
反射可能是正确的答案:
Method method = MyParser.class.getDeclaredMethod("qux");
ParseTree tree = (ParseTree) method.invoke(parser);
适合替换:
ParseTree tree = parser.qux();