0

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

有什么想法吗?

4

2 回答 2

1

我创建了一个关于如何测试 ANTLR 访问者的示例项目(使用 Mockito&TestNG)。完整的源代码可以在 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方法的实现)。

于 2015-03-21T15:55:30.650 回答
0

反思可能是这里的正确答案:

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

适合替代:

ParseTree tree = parser.qux();
于 2014-06-28T15:20:13.560 回答