はじめに
みなさんは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等を参照してください。