Hierarchical Visitor Pattern and State Management

I am trying to find a good way to manage state with the hierarchical visitor pattern and ANTLRs autogenerated base visitor class. While the example below is something silly I made up, I believe it helps get the point across about which concepts I would like to address.

As an example lets say we have a class:

public class JavaClassVisitor extends JavaBaseVisitor<List<String>> {

    private Map<String, String> dict = new HashMap<>();
    dict.put("public", "I FOUND A PUBLIC SPECIFIER!");
    dict.put("private", "I FOUND A PRIVATE SPECIFIER")

    private List<String> descriptions = new ArrayList<>();

    @Override
    public List<String> visitParseContext(ParseContext ctx){
         visitChildren(ctx);
         return descriptions;
    }

    @Override
    public List<String> visitClassDeclaration(ClassDeclarationContext ctx){
        IdentifierContext idCtx = ctx.Identifier();
        if(idCtx != null){
          String accessSpecifier = idCtx.getText();
          String description = dict.get(accessSpecifier);
          descriptions.add(description);
        } 
        return visitChildren(ctx); 
    }

    @Override
    public List<String> visitMethodDeclaration(MethodDeclarationContext ctx){
        IdentifierContext idCtx = ctx.Identifier();
        if(idCtx != null){
          String accessSpecifier = idCtx.getText();
          String description = dict.get(accessSpecifier);
          descriptions.add(description);
        }
        return visitChildren(ctx);
    }

}

Now note that this class is not very testable, nor is managing the state at the top of the class desirable. However I am having a hard time coming up with a way to test the visit methods. Using Junit/Mockito, you could do the following:

public class JavaClassVisitorTest(){

  @Mock
  private ClassDeclarationContext classDecCtx;

  @Mock
  private IdentifierContext idCtx;

  @Before
  public void setup(){
     MockitoAnnotations.init(this);
  }    

  @Test
  public void test(){

     doReturn("public")
      .when(idCtx)
      .Identifier();

     doReturn(idCtx)
      .when(classDecCtx)
      .Identifier();

     JavaClassVisitor vstr = new JavaClassVisitor();
     vstr.visitClassDeclaration(classDecCtx);


  }

}

I would ideally like to check that, for example, a description was added if idCtx existed, but I can’t using this method. I Am I holding the pattern wrong for what I would like to accomplish? Any insight into how to better manage state is appreciated.

Answer

Testing that is not too difficult.

Assuming, you want to do some unit tests. Then you’re only looking at the method implementations and mock everything else. For example, visitClassDeclaration.

public void thatItProperlyCollectsDescriptionsForVisitedClassDeclarations() {
  // Given
  ClassDeclarationContext classDeclMock = mock(ClassDeclarationContext.class);
  JavaClassVisitor victim = spy(new JavaClassVisitor ());

  // When
  victim.visitClassDeclaration(classDeclMock)

  // Then
  assertTrue(victim.getDescriptions().contains(theExpectedString));  // I leave that to you :D
  verify(victim).visitChildren(classDeclMock);  // it calls the visit children method
}

I think you get the point: the method needs to add something to the description list and call the visitChildren method. Same for the others.

For an Integration test, you could construct a test object which is a more complete hierarchy of Parse, Class and Method declaration contexts. But I’d leave the main work on the unit tests and maybe test one happy path with a simple mock that has one child on each level – just to be sure that all hierarchy levels are really visited.

(code example should be considered as pseudo code, I didn’t test it)

Leave a Reply

Your email address will not be published. Required fields are marked *