Testing grammars with JUnit

Testing a Parser

Be sure to define a subclass of IANTLRFrontEnd and to declare an ANTLRTester in your test-case class. (See the usage page for further instructions.)

Also, test your lexer first.

AST Assertions

It's harder to make simple assertions about a whole AST (Abstract Syntax Tree) compared to making assertions on tokens.

The helpful method from org.norecess.antlr.Assert is assertTree. You now make assertions on

  • the type of the root of the AST, and
  • the preorder traversal of the AST with heavily parenthesized text.

Here's an example parsing an infix + expression:

assertTree(MyOwnParser.EXPRESSION, "(+(1)(x))",
  myTester.scanInput("1 + x").parseAs("expression"));

The parseAs("expression") takes the token stream from the scanned input and parses that stream starting (in this case) with the expression production. This allows you to test your grammar at the finest detail without having extraordinary input and preorder traversals.

MyOwnParser.EXPRESSION is the type of the root of the expected AST. You can always refer to these types from your parser class whether their imaginary or lexer tokens.

(+(1)(x)) is the preorder traversal built up with lots of parentheses and the tokens' texts.

Examples

Here I test various ways to build up a tuple:

@Test
public void shouldRecognizeATupleAsAProgram() {
    assertTree(MyOwnParser.TUPLE, "(TUPLE)", myTester.scanInput("()")
            .parseAs("program"));
    assertTree(MyOwnParser.TUPLE, "(TUPLE(1))", myTester.scanInput(
            "(1)").parseAs("program"));
    assertTree(MyOwnParser.TUPLE, "(TUPLE(1)(2)(3))", myTester
            .scanInput("(1,2, 3)").parseAs("program"));
}

Note that the commas are recognized by the parser in the last assertion, but they are not included in the AST (as evidenced by the preorder traversal).

Testing failures is a bit clumsy:

@Test(expected = Exception.class)
public void shouldFailOnInitialComma() {
    myTester.scanInput("(,2, 3)").parseAs("program");
}