我正试图找到一个好的方法来管理状态与分层访问者模式和ANTLRs自动生成的基础访问者类.虽然下面的例子是我编造的一些愚蠢的东西,但我相信它有助于理解我想解决的概念。
作为一个例子,让我们说我们有一个类。
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);
}
}
现在请注意,这个类的可测试性不高,管理类顶部的状态也不可取。然而,我很难想出一种方法来测试访问方法.使用JunitMockito,你可以做以下事情。
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);
}
}
我理想的情况是想检查,例如,如果idCtx存在,则添加了一个描述,但我不能使用这种方法。我是不是对我想实现的目标持有错误的模式?任何关于如何更好地管理状态的见解都是感激的。
测试,并不是太难。
假设,你想做一些单元测试。那么你就只看方法的实现,其他的都要模拟。比如说 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
}
我想你明白了:该方法需要在描述列表中添加一些东西,并调用visitChildren方法。其他的方法也是一样。
对于集成测试,你可以构造一个测试对象,它是一个比较完整的层次结构,包括Parse、Class和Method声明上下文。但我会把主要工作留在单元测试上,也许用一个简单的mock来测试一个快乐的路径,每个层次上都有一个孩子--只是为了确保所有的层次结构层次真的被访问了。
(代码示例应视为伪代码,我没有测试)