Javaアノテーションプロセッサの自動ユニットテストを書く方法は?
-
06-07-2019 - |
質問
Java注釈プロセッサを試しています。 <!> quot; JavaCompiler <!> quot;を使用して統合テストを作成できます。 (実際、私は現在<!> quot; hickory <!> quot;を使用しています)。コンパイルプロセスを実行し、出力を分析できます。問題:注釈プロセッサにコードがなくても、単一のテストが約0.5秒間実行されます。これは、TDDスタイルで使用するには長すぎます。
依存関係のモックアップは非常に難しいようです(<!> quot; javax.lang.model.element <!> quot;パッケージ全体をモックアウトする必要があります)。誰かが注釈プロセッサ(Java 6)の単体テストの作成に成功しましたか?そうでない場合...あなたのアプローチはどうなりますか?
解決
あなたは間違いなく、アノテーション処理APIを(easymockのようなモックライブラリで)モックするのは苦痛です。私はこのアプローチを試みましたが、かなり急速に故障しました。多くのメソッド呼び出しの期待値にセットアップする必要があります。テストは維持できなくなります。
状態ベースのテストアプローチは、私にとってはかなりうまくいきました。 javax.lang.model。*テストに必要なAPI 。 (これは<!> lt; 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
。 (サポートされていない操作はすべて例外をスローします。)
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(); }
};
}
残りのテストでは、テスト対象のクラスの動作を検証します。
Method actual = new Method(environment(), methodExecutableElement);
Method expected = new Method(..);
assertEquals(expected, actual);
Quickcheck @Samplesおよび@Iterablesソースコードジェネレーターテストのソースコード。 (コードはまだ最適ではありません。Methodクラスには多くのパラメーターが必要であり、Parameterクラスは独自のテストではなく、Methodテストの一部としてテストされます。それでもアプローチを説明する必要があります。)
ヴィエルGl <!>#252; ck!
他のヒント
これは古い質問ですが、アノテーションプロセッサのテストの状態は改善されていないようであるため、コンパイルをリリースしました今日のテスト。最高のドキュメントはパッケージにあります-info.java 。ただし、一般的な考え方は、注釈プロセッサで実行したときにコンパイル出力をテストするための流fluentな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);
これは明らかにモッキングよりもはるかに単純であり、一般的な統合テストとは異なり、すべての出力はメモリに保存されます。
jOORは小さなJavaリフレクションライブラリであり、javax.tool.JavaCompiler
内のメモリ内JavaコンパイルAPIへのアクセスを簡素化します。単体テストにこのサポートを追加しました 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);
}
}
オプションは、すべてのテストを1つのクラスにバンドルすることです。コンパイルなどの0.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環境全体を模擬するというトーマスの提案は、純粋な単体テストにつながります。代わりに、プロセッサが実際にjavac内でどのように実行されるかを確認する統合テストをさらに作成し、正しいことを保証しますが、単にディスクファイルを避けたい場合があります。これを行うには、モックJavaFileManager
を記述する必要がありますが、残念ながら見た目ほど簡単ではなく、便利な例もありませんが、Element
インターフェースのような他のものをモックする必要はありません。
同様の状況にあったので、アバターライブラリを作成しました。コンパイルなしの純粋な単体テストのパフォーマンスは得られませんが、正しく使用した場合、パフォーマンスに大きな影響はありません。
アバターを使用すると、ソースファイルを記述し、注釈を付け、単体テストの要素に変換できます。これにより、javacを手動で呼び出すことなく、Elementオブジェクトを使用するメソッドとクラスを単体テストできます。