如何为java注释处理器编写自动化unit testing?
我正在尝试使用java注释处理器。 我能够使用“JavaCompiler”编写集成测试(实际上我现在正在使用“hickory”)。 我可以运行编译过程并分析输出。 问题:即使我的注释处理器中没有任何代码,单个测试也会运行大约半秒钟。 这在TDD风格中使用它太长了。
嘲笑依赖关系对我来说似乎很难(我必须模拟整个“javax.lang.model.element”包)。 有人成功为注释处理器(Java 6)编写unit testing吗? 如果不是……你的方法是什么?
你是嘲笑注释处理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);
静态工厂方法在实现javax.lang.model。*类的Model
中定义。 例如declaredType
。 (所有不受支持的操作都会抛出exception。)
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 accept(TypeVisitor 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(); } }; }
测试的其余部分validation了被测试类的行为。
Method actual = new Method(environment(), methodExecutableElement); Method expected = new Method(..); assertEquals(expected, actual);
您可以查看Quickcheck @Samples和@Iterables源代码生成器测试的源代码 。 (代码不是最优的.Session类有许多参数,而且参数类没有在自己的测试中测试,而是作为方法测试的一部分。它应该说明这种方法。)
VielGlück!
这是一个老问题,但似乎注释处理器测试的状态没有变得更好,所以我们今天发布了编译测试 。 最好的文档在package-info.java中 ,但一般的想法是,当使用注释处理器运行时,有一个流畅的API用于测试编译输出。 例如,
ASSERT.about(javaSource()) .that(JavaFileObjects.forResource("HelloWorld.java")) .processedWith(new MyAnnotationProcessor()) .compilesWithoutError() .and().generatesSources(JavaFileObjects.forResource("GeneratedHelloWorld.java"));
测试处理器生成的文件与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);
这显然比模拟简单得多,与典型的集成测试不同,所有输出都存储在内存中。
一个选项是将所有测试捆绑在一个类中。 对于一组给定的测试,编译等的半秒是一个常数,我认为测试的实际测试时间是可以忽略的。
我使用了http://hg.netbeans.org/core-main/raw-file/default/openide.util.lookup/test/unit/src/org/openide/util/test/AnnotationProcessorTestUtils.java虽然这是基于在java.io.File
为了简单起见,你会抱怨性能开销。
托马斯建议嘲笑整个JSR 269环境将导致纯粹的unit testing。 您可能希望编写更多的集成测试来检查您的处理器如何在javac中实际运行,从而更好地保证它是正确的,但只是想避免使用磁盘文件。 这样做需要你编写一个模拟JavaFileManager
,遗憾的是它并不像看起来那么容易,我没有任何实用的例子,但是你不需要模拟像Element
接口这样的其他东西。
我处于类似的情况,所以我创建了阿凡达库。 它不会为您提供没有编译的纯unit testing的性能,但如果使用正确,您不应该看到很多性能损失。
“头像”允许您编写源文件,对其进行注释,并将其转换为unit testing中的元素。 这允许您对使用Element对象的测试方法和类进行单元化,而无需手动调用javac。