测试antlr4访客规则

时间:2014-06-28 00:08:34

标签: unit-testing java-7 antlr4 table-driven

我有一个相当复杂的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()不是静态的。

有什么想法吗?

2 个答案:

答案 0 :(得分:1)

我创建了一个关于如何测试ANTLR访问者的示例项目(使用Mockito&amp; TestNG)。 The full source code can be found on 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方法的实现)。

答案 1 :(得分:0)

反思可能是正确答案:

Method method = MyParser.class.getDeclaredMethod("qux");
ParseTree tree = (ParseTree) method.invoke(parser);

适合替代:

ParseTree tree = parser.qux();