测试 AbstractProcessor(Java 在编译时的注解处理)
Testing AbstractProcessor (Java's annotation handling at compile time)
我正在编写一个依赖于 AbstractProcessor
class 的库,因为我想编写一个很棒的库,所以我也希望有一个很好的覆盖范围。由于 预处理器 在编译时工作,我不确定如何测试该代码。
我脑子里有一些构建应该失败的测试场景。但是我该如何测试呢?我是否必须执行 gradle 并检查退出代码?是否有一种干净的方法来验证构建失败是由预期原因引起的,还是我还需要编写某种解析器?恕我直言,为了获得良好的覆盖率,这将是一笔巨大的开销。
对于那些需要示例的人:
@SupportedAnnotationTypes("eu.rekisoft.java.preprocessor.Decorator")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ExamplePreprocessor extends AbstractProcessor {
public ExamplePreprocessor() {
super();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for(Element elem : roundEnv.getElementsAnnotatedWith(Decorator.class)) {
Method method = Method.from((ExecutableElement)elem);
if(!method.matchesTypes(String.class, StringBuilder.class)) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "No! " + elem + " has the wrong args!");
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Yey " + elem + " this is fine!");
}
}
return true; // no further processing of this annotation type
}
}
这里是一个无法编译的class:
public class AnnotationedExample {
@Decorator
public int breakBuild() {
return -1;
}
@Decorator
public String willCompile(StringBuilder sb) {
return null;
}
}
最后是无聊的注释:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Decorator {
}
您也可以在 GitHub 检出项目,您只需要检出项目并执行 gradlew cJ
。您可能需要修复 linux 和 mac.
脚本缺少的 x 权限
之前的团队编写编译器和注解处理器已经解决了这个问题,因此您可以重复使用他们的解决方案。
javac 团队使用了一个名为 jtreg 的工具。
例如,这里每个都有一个jtreg test that compiles a source file 6 times with different command-line arguments, indicating a different expected compiler output。
Checker Framework team designed a testing framework specifically for annotation processors. Here is a test case using that framework, where special //::
comments indicate the lines where the annotation processor is expected to issue a warning or an error.
Google 有一个很好的 API 用于测试编译。 (https://github.com/google/compile-testing)。这是一个测试 .java 文件是否可以使用 junit.
处理器编译的示例
package org.coffeebag.processor;
import com.google.testing.compile.JavaFileObjects;
import com.google.testing.compile.JavaSourceSubjectFactory;
import org.junit.Test;
import java.io.File;
import java.net.MalformedURLException;
import static org.truth0.Truth.ASSERT;
public class ExampleTest {
@Test
public void EmptyClassCompiles() throws MalformedURLException {
final MyProcessor processor = MyProcessor.testMode();
File source = new File("path/to/test/file.java");
ASSERT.about(JavaSourceSubjectFactory.javaSource())
.that(JavaFileObjects.forResource(source.toURI().toURL()))
.processedWith(processor)
.compilesWithoutError();
}
}
我们编写了一个简化的 API 来帮助访问 javax.tools.JavaCompiler
API 进行内存中 Java 编译,这允许编写简单的基于 JUnit 的测试。在你的例子中,你可以写:
@Test
public void testFailAnnotationProcessing() {
ExamplePreprocessor p = new ExamplePreprocessor();
try {
Reflect.compile(
"com.example.test.FailAnnotationProcessing",
"package com.example.test; " +
"public class FailAnnotationProcessing { " +
" @com.example.Decorator " +
" public int breakBuild() { " +
" return -1; " +
" } " +
"}",
new CompileOptions().processors(p)
).create().get();
Assert.fail();
}
catch (ReflectException expected) {}
}
@Test
public void testSucceedAnnotationProcessing() {
ExamplePreprocessor p = new ExamplePreprocessor();
Reflect.compile(
"com.example.test.SucceedAnnotationProcessing",
"package com.example.test; " +
"public class SucceedAnnotationProcessing { " +
" @com.example.Decorator " +
" public String willCompile(StringBuilder sb) { " +
" return null; " +
" } " +
"}",
new CompileOptions().processors(p)
).create().get();
}
我正在编写一个依赖于 AbstractProcessor
class 的库,因为我想编写一个很棒的库,所以我也希望有一个很好的覆盖范围。由于 预处理器 在编译时工作,我不确定如何测试该代码。
我脑子里有一些构建应该失败的测试场景。但是我该如何测试呢?我是否必须执行 gradle 并检查退出代码?是否有一种干净的方法来验证构建失败是由预期原因引起的,还是我还需要编写某种解析器?恕我直言,为了获得良好的覆盖率,这将是一笔巨大的开销。
对于那些需要示例的人:
@SupportedAnnotationTypes("eu.rekisoft.java.preprocessor.Decorator")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class ExamplePreprocessor extends AbstractProcessor {
public ExamplePreprocessor() {
super();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for(Element elem : roundEnv.getElementsAnnotatedWith(Decorator.class)) {
Method method = Method.from((ExecutableElement)elem);
if(!method.matchesTypes(String.class, StringBuilder.class)) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "No! " + elem + " has the wrong args!");
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Yey " + elem + " this is fine!");
}
}
return true; // no further processing of this annotation type
}
}
这里是一个无法编译的class:
public class AnnotationedExample {
@Decorator
public int breakBuild() {
return -1;
}
@Decorator
public String willCompile(StringBuilder sb) {
return null;
}
}
最后是无聊的注释:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Decorator {
}
您也可以在 GitHub 检出项目,您只需要检出项目并执行 gradlew cJ
。您可能需要修复 linux 和 mac.
之前的团队编写编译器和注解处理器已经解决了这个问题,因此您可以重复使用他们的解决方案。
javac 团队使用了一个名为 jtreg 的工具。 例如,这里每个都有一个jtreg test that compiles a source file 6 times with different command-line arguments, indicating a different expected compiler output。
Checker Framework team designed a testing framework specifically for annotation processors. Here is a test case using that framework, where special //::
comments indicate the lines where the annotation processor is expected to issue a warning or an error.
Google 有一个很好的 API 用于测试编译。 (https://github.com/google/compile-testing)。这是一个测试 .java 文件是否可以使用 junit.
处理器编译的示例package org.coffeebag.processor;
import com.google.testing.compile.JavaFileObjects;
import com.google.testing.compile.JavaSourceSubjectFactory;
import org.junit.Test;
import java.io.File;
import java.net.MalformedURLException;
import static org.truth0.Truth.ASSERT;
public class ExampleTest {
@Test
public void EmptyClassCompiles() throws MalformedURLException {
final MyProcessor processor = MyProcessor.testMode();
File source = new File("path/to/test/file.java");
ASSERT.about(JavaSourceSubjectFactory.javaSource())
.that(JavaFileObjects.forResource(source.toURI().toURL()))
.processedWith(processor)
.compilesWithoutError();
}
}
我们编写了一个简化的 API 来帮助访问 javax.tools.JavaCompiler
API 进行内存中 Java 编译,这允许编写简单的基于 JUnit 的测试。在你的例子中,你可以写:
@Test
public void testFailAnnotationProcessing() {
ExamplePreprocessor p = new ExamplePreprocessor();
try {
Reflect.compile(
"com.example.test.FailAnnotationProcessing",
"package com.example.test; " +
"public class FailAnnotationProcessing { " +
" @com.example.Decorator " +
" public int breakBuild() { " +
" return -1; " +
" } " +
"}",
new CompileOptions().processors(p)
).create().get();
Assert.fail();
}
catch (ReflectException expected) {}
}
@Test
public void testSucceedAnnotationProcessing() {
ExamplePreprocessor p = new ExamplePreprocessor();
Reflect.compile(
"com.example.test.SucceedAnnotationProcessing",
"package com.example.test; " +
"public class SucceedAnnotationProcessing { " +
" @com.example.Decorator " +
" public String willCompile(StringBuilder sb) { " +
" return null; " +
" } " +
"}",
new CompileOptions().processors(p)
).create().get();
}