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>