Fight the Future

Java言語とJVM、そしてJavaエコシステム全般にまつわること

アノテーショントランスフォーマー-TestNGをさらに深く理解してみる(2)

打って変わって、今回は実装の話。
TestNGでは実行時に@Testのさまざまな属性の値を変えることができる。
それがアノテーショントランスフォーマー
TestNGのバージョン5.3からの目玉機能。
サンプルで試してみる。


まずはテストクラス。@Testのtimeout属性を変えたいので、わかりやすいようにThread.Sleep()を呼び出してる。
アサートに意味なし。

package net.kronosjp.testng.transformer;

import static org.testng.Assert.*;

import org.testng.annotations.Test;

public class Sample {
	@Test
	public void test() throws InterruptedException {
		Thread.sleep(3000);
		assertTrue(true);
	}
}

これをアノテーショントランスフォーマーとか関係なく普通に実行すると、当然成功する。

PASSED: test

===============================================
    net.kronosjp.testng.transformer.Sample
    Tests run: 1, Failures: 0, Skips: 0
===============================================

ではアノテーショントランスフォーマーを作ってみる。
IAnnotationTransformerインターフェースを実装する。
transform()メソッドがあるだけ。

package net.kronosjp.testng.transformer;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.internal.annotations.IAnnotationTransformer;
import org.testng.internal.annotations.ITest;

public class SampleAnnotationTransformer implements IAnnotationTransformer {

	public void transform(ITest annotation, Class testClass,
			Constructor testConstructor, Method testMethod) {
		System.out.println("★テストメソッド名★" + testMethod.getName());
		annotation.setTimeOut(1000);
	}
}

引数に@TestのインスタンスであるITestオブジェクト、そしてリフレクションのオブジェクトが渡される。
だからメソッド名で判断して属性の値を変えたりもできる。
今回はタイムアウトを1000ミリ秒にしてみた。


テストメソッドでは3000ミリ秒待機してる。
ところがアノテーショントランスフォーマーでタイムアウトを1000ミリ秒に設定したから、テストはタイムアウトオーバーで失敗することになる。
@Test(timeout = 1000)と同じ意味。


じゃあアノテーショントランスフォーマーを使ってテストを実行するために、設定ファイルを書く。

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="samplesuite">
	<method-selectors>
		<method-selector>
			<selector-class
				name="net.kronosjp.testng.transformer.SampleAnnotationTransformer"></selector-class>
		</method-selector>
	</method-selectors>
	<test verbose="2" name="sampletest" annotations="JDK">
		<classes>
			<class name="net.kronosjp.testng.transformer.Sample"></class>
		</classes>
	</test>
</suite>

気をつけることは、listener要素じゃなくてmethod-selector要素を使うこと。listener要素にアノテーショントランスフォーマーを書いても動作しなかった。
実行すると次のようになる。

★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
★テストメソッド名★test
FAILED: test
org.testng.internal.thread.ThreadTimeoutException: Method public void net.kronosjp.testng.transformer.Sample.test() throws java.lang.InterruptedException didn't finish within the time-out 1000
... Removed 18 stack frames

===============================================
    sampletest
    Tests run: 1, Failures: 1, Skips: 0
===============================================

「java.lang.InterruptedException didn't finish within the time-out 1000」の表示のとおり、タイムアウトでテストが失敗してる。
けど、なんでtransform()メソッドが何回も呼ばれるんだろう??
ちょっとよくわからない。


プログラムでこうした値を一括で制御できることにはメリットがあると思う。
リフレクションで制御できるのも魅力になっている。