如何为java注释处理器编写自动化单元测试?

Arn*_*sch 26 java unit-testing annotations annotation-processing

我正在尝试使用java注释处理器.我能够使用"JavaCompiler"编写集成测试(事实上我现在正在使用"hickory").我可以运行编译过程并分析输出.问题:即使我的注释处理器中没有任何代码,单个测试也会运行大约半秒钟.这在TDD风格中使用它太长了.

嘲笑依赖关系对我来说似乎很难(我必须模拟整个"javax.lang.model.element"包).有人成功为注释处理器(Java 6)编写单元测试吗?如果不是......你的方法是什么?

gk5*_*885 43

这是一个老问题,但似乎注释处理器测试的状态没有变得更好,所以我们今天发布了编译测试.最好的文档在package-info.java中,但一般的想法是,当使用注释处理器运行时,有一个流畅的API用于测试编译输出.例如,

ASSERT.about(javaSource())
    .that(JavaFileObjects.forResource("HelloWorld.java"))
    .processedWith(new MyAnnotationProcessor())
    .compilesWithoutError()
    .and().generatesSources(JavaFileObjects.forResource("GeneratedHelloWorld.java"));
Run Code Online (Sandbox Code Playgroud)

测试处理器生成匹配的文件GeneratedHelloWorld.java(类路径上的黄金文件).您还可以测试处理器是否产生错误输出:

JavaFileObject fileObject = JavaFileObjects.forResource("HelloWorld.java");
ASSERT.about(javaSource())
    .that(fileObject)
    .processedWith(new NoHelloWorld())
    .failsToCompile()
    .withErrorContaining("No types named HelloWorld!").in(fileObject).onLine(23).atColumn(5);
Run Code Online (Sandbox Code Playgroud)

这显然比模拟简单得多,与典型的集成测试不同,所有输出都存储在内存中.


Tho*_*ung 9

你是嘲笑注释处理API(使用像easymock这样的模拟库)是很痛苦的.我试过这种方法,它很快崩溃了.您必须设置许多方法调用期望.测试变得不可维护.

一个基于状态的测试方法为我工作得非常好.我必须实现我测试所需javax.lang.model.*API部分.(那只是<350行代码.)

这是启动javax.lang.model对象的测试的一部分.在设置之后,模型应该与Java编译器实现处于相同的状态.

DeclaredType typeArgument = declaredType(classElement("returnTypeName"));
DeclaredType validReturnType = declaredType(interfaceElement(GENERATOR_TYPE_NAME), typeArgument);
TypeParameterElement typeParameter = typeParameterElement();
ExecutableElement methodExecutableElement = Model.methodExecutableElement(name, validReturnType, typeParameter);
Run Code Online (Sandbox Code Playgroud)

静态工厂方法在Model实现javax.lang.model.*类的类中定义.例如declaredType.(所有不受支持的操作都会抛出异常.)

public static DeclaredType declaredType(final Element element, final TypeMirror... argumentTypes) {
    return new DeclaredType(){
        @Override public Element asElement() {
            return element;
        }
        @Override public List<? extends TypeMirror> getTypeArguments() {
            return Arrays.asList(argumentTypes);
        }
        @Override public String toString() {
            return format("DeclareTypeModel[element=%s, argumentTypes=%s]",
                    element, Arrays.toString(argumentTypes));
        }
        @Override public <R, P> R accept(TypeVisitor<R, P> v, P p) {
            return v.visitDeclared(this, p);
        }
        @Override public boolean equals(Object obj) { throw new UnsupportedOperationException(); }
        @Override public int hashCode() { throw new UnsupportedOperationException(); }

        @Override public TypeKind getKind() { throw new UnsupportedOperationException(); }
        @Override public TypeMirror getEnclosingType() { throw new UnsupportedOperationException(); }
    };
}
Run Code Online (Sandbox Code Playgroud)

测试的其余部分验证了被测试类的行为.

Method actual = new Method(environment(), methodExecutableElement);
Method expected = new Method(..);
assertEquals(expected, actual);
Run Code Online (Sandbox Code Playgroud)

您可以查看Quickcheck @Samples和@Iterables源代码生成器测试的源代码.(代码还不是最优的.Session类有很多参数,而且参数类没有在自己的测试中测试,而是作为方法测试的一部分.它应该说明这种方法.)

VielGlück!


Luk*_*der 5

jOOR 是一个小型 Java 反射库,它还提供对javax.tool.JavaCompiler. 我们添加了对此的支持以对jOOQ 的注释处理器进行单元测试。您可以轻松编写这样的单元测试:

@Test
public void testCompileWithAnnotationProcessors() {
    AProcessor p = new AProcessor();

    try {
        Reflect.compile(
            "org.joor.test.FailAnnotationProcessing",
            "package org.joor.test; " +
            "@A " +
            "public class FailAnnotationProcessing { " +
            "}",
            new CompileOptions().processors(p)
        ).create().get();
        Assert.fail();
    }
    catch (ReflectException expected) {
        assertFalse(p.processed);
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的例子取自这篇博客文章