はじめに
みなさんはJUnit使ってますか?
Java用のテスティングフレームワークはTestNGなどもありますが、JUnitを使っている人が多いと思います。
そんなJUnitの@Rule
・@ClassRule
について、備忘録として自分なりにまとめてみました。
間違っている場合もありますのでご容赦ください。
なお、実装例のpackage
文・import
文は省略しています。
概要
@Rule
は@Before
・@After
、
@ClassRule
は@BeforeClass
・@AfterClass
の代替だそうです。
実際にはもっといろいろなことが出来るようです。
今後@Before
や@After
などが非推奨となることはないとは思いますが、読めるようにはしておきたいですね。
TestRule
クラスについて
TestRule
クラスを継承したクラスのインスタンスを、テストクラスのpublic
フィールドに代入しておくことによって、テストに関する便利な機能が使えます。
(@ClassRule
の場合はstatic
にもしなければなりません。)
下記は、テストケース内で動的にテストケース名を取得する例です。
public class TestClass {
// テストケース名を取得するルールを定義
@Rule
public TestName testName = new TestName();
@Test
public void testMethod() {
System.out.println("Method Name is " + testName.getMethodName());
}
}
Method Name is testMethod
前処理・後処理の記述方法について
前処理・後処理を書くには、ExternalResource
クラスが便利です。
クラス名にResource
と付いてますが、特にリソースに関する処理でなくても大丈夫です。
このクラスをインスタンス化する際に、before
・after
メソッドをオーバーライドすることで、それぞれ前処理・後処理を定義できます。
よくある使い方としては、ログに関する処理です。
下記では、例としてテストクラス・ケース実行の前後にテストケース名を出力しています。
テストケース名はExternalResource
クラスと前述のTestName
クラスで出力し、テストクラス名はTestRule
クラスを自分で拡張して取得・出力しています。
public class TestClass {
// テストクラス名を出力するルールを作成
@ClassRule
public static TestRule classRule = new TestRule() {
@Override
public Statement apply(Statement arg0, Description arg1) {
// Statementクラスの匿名クラスを作成し、
// テストクラス名を出力するようにevaluateメソッドをオーバーライドする。
return new Statement() {
private final Description description = arg1;
private final Statement base = arg0;
@Override
public void evaluate() throws Throwable {
// テスト実行前に行う処理
System.out.println("Class : " + this.description.getClassName() + " start.");
// テストを実行する
// このメソッドを実行すると、実際にテストケースが実行される。
base.evaluate();
// テスト実行後に行う処理
System.out.println("Class : " + this.description.getClassName() + "end.");
}
};
}
};
// テストケース名を出力するルール
@Rule
public TestRule rule = new ExternalResource() {
@Override
public void before() {
System.out.println("Method : " + testName.getMethodName() + "start.");
}
@Override
public void after() {
System.out.println("Method : " + testName.getMethodName() + "end.");
}
};
@Rule
public TestName testName = new TestName();
@Test
public void testMethod1() {
System.out.println("testMethod1 runnning...");
}
@Test
public void testMethod2() {
System.out.println("testMethod2 runnning...");
}
Class : junit.TestClass start.
Method : testMethod1start.
testMethod1 runnning...
Method : testMethod1end.
Method : testMethod2start.
testMethod2 runnning...
Method : testMethod2end.
Class : junit.TestClassend.
テストクラス名を出力するルールについてですが、
-
TestRule
クラスの匿名クラスを作成し、 -
apply
メソッドをオーバーライドし、 - その戻り値として
Statement
クラスの匿名クラスを作成し、 -
evaluate
メソッドをオーバーライドし、 - その中でテストクラス名の出力を行なっています。
ややこしいですが、やっていることはそれほど難しくないのですぐわかると思います。
@Before
・@After
の代わりとして使うだけなら、このコードをコピペして、base.evaluate();
の前後にやりたい処理を記述するだけです。
2016/09/09 追記
Java8のラムダ式を使うと、以下のように(比較的)簡潔に書けます。
@Rule
public TestRule logRule = (Statement statement, Description description) -> new Statement() {
@Override
public void evaluate() throws Throwable {
System.out.println("Start : " + description.getClassName() + "#" + description.getMethodName());
try {
statement.evaluate();
} finally {
// 例外発生時にも終了ログを出力する
System.out.println("End : " + description.getClassName() + "#" + description.getMethodName());
}
}
};
補足
このコードの@Rule
や@ClassRule
は、親クラスに書いていてもちゃんと実行中のテストクラス・ケース名を取得してくれます。
おわりに
ログ処理、DB処理等で使うことがメインになりそうですが、もっとおもしろい使い方とかあるのか調べてみたいです。
(AOPのようなことができるみたいです。)
詳しくはJUnitの公式ドキュメントやJavadoc等を参照してください。