DbUnit + TestNGのライブラリでは、@SetUpOperationなどのアノテーションを使えるわけですが、これはTestNGでリスナーインタフェースが用意されているおかげです。
TestNGでは、リスナーインタフェースが4つ用意されています。
リスナー |
説明 |
IMethodInterceptor |
テストメソッド実行前に呼び出されるリスナー。実行するテストメソッドを選択することができる。 |
IReporter |
テスト結果のレポート処理に関するリスナー。 |
ISuiteListener |
テストスイートの実行前と終了後に呼び出されるリスナー。 |
ITestListener |
各テストやテストメソッド実行前と終了後に呼び出されるリスナー。 |
ちなみにこれら4つはすべてITestNGListenerのサブインタフェースです。
なので、たとえばテストメソッドごとにつける@SetUpOperationなどは、テストメソッドごとに解釈する必要があるので、ITestListenerを使います。
ITestLisnerには7つのリスナーメソッドがあります。
メソッド |
説明 |
onStart(ITestContext context) |
テストクラスをインスタンス化した後に呼び出される。 |
onFinish(ITestContext context) |
すべてのテストを実行した後に呼び出される。 |
onTestStart(ITestResult result) |
各テストメソッドを実行する前に呼び出される。 |
onTestFailedButWithinSuccessPercentage(ITestResult result) |
テストが成功確率以内なら呼び出される。*1 |
onTestFailure(ITestResult result) |
テストメソッドが失敗したら呼び出される。 |
onTestSkipped(ITestResult result) |
テストメソッドをスキップしたら呼び出される。 |
onTestSuccess(ITestResult result) |
テストメソッドが成功したら呼び出される。 |
このうち、テストメソッドごとにアノテーションを解釈するので、onTestStart()を実装する必要があります。
さて、@SetUpOperationですが、こんなアノテーションです。
@Retention(RUNTIME)
@Target(METHOD)
public @interface SetUpOperation {
DatabaseOperationType value() default DatabaseOperationType.NONE;
String pathname() default "";
String extension() default "";
}
DatabaseOperationTypeはCLEAN_INSERTとかREFLESHとかのEnumです。pathnameは初期データファイルのパス、extensionは拡張子を指定します。
で、このアノテーションをonTestStart()で解釈します。
public class DbUnitNGTestListener implements ITestListener {
public void onTestStart(ITestResult result) {
SetUpOperation setUpOperation =
this.getAnnotation(result, SetUpOperation.class);
if (setUpOperation == null) {
return;
}
DatabaseOperationType type = setUpOperation.value();
String extension = setUpOperation.extension();
String pathName = setUpOperation.pathname();
readFileForDatabase(result, type, extension, pathName);
}
protected <T extends Annotation> T getAnnotation(ITestResult result,
Class<T> annotationClass) {
ITestNGMethod testNGMethod = result.getMethod();
Method method = testNGMethod.getMethod();
return method.getAnnotation(annotationClass);
}
onTestStart()には引数としてITestResultオブジェクトが渡されます。
この中に、今から実行するテストメソッドが入っています。
呼び出しているgetAnnotation()メソッドを見てみましょう。
ITestResult#getMethod()を呼び出します。この戻り値はjava.lang.reflect.Methodではなく、ITestNGMethodです。
ITestNGMethodからアノテーションはとれないので、ITestMethod#getMethod()を呼び出してMethodを取得します。
そして、Method#getAnnotaion(Class)でアノテーションクラスであるSetUpOperation.classを渡し、アノテーションを取得します。
もし、@SetUpOperationがついていないメソッドなら、nullが返ります。
あとはアノテーションから属性の値(DatabaseOperationType, pathname, extension)を取得し、ファイルを読み出してデータベースに登録してます(readFileForDatabase())。
このように、リスナーとアノテーションを使えば、うまくテスト実行前の初期化処理ができます。
こういう仕組みは、JUnitにはないので、TestNGの利点だと思います。
ちなみに、作ったリスナーを利用するには、testng.xmlに記述します。
<suite name="DbUnitNG">
<listeners>
<listener class-name="org.dbunitng.listeners.DbUnitNGTestListener"></listener>
</listeners>
</suite>