JUnit API探訪:@Rule, @ClassRule アノテーション
JUnit4.8で@Ruleアノテーションが、4.9でそれのクラス版となる@ClassRuleアノテーションが使えるようになってみたいですね。
調べて行くと、@Ruleアノテーション及び@ClassRuleアノテーションは、TestRuleというインターフェース(での用途)に関連して利用されるもののようです。
@Rule | @Before, @Afterアノテーションの代替 | メソッドレベルでTestRuleと併せて利用 |
@ClassRule | @BeforeClass, @AfterClassアノテーションの代替 | クラスレベルでTestRuleと併せて利用 |
また、4.7時点では @RuleはMethodRuleに対して利用するものでしたが、4.9にて非推奨となった模様です。(MethodRuleが非推奨→TestRuleを推奨)
@Rule アノテーション
ルールによりテストごとにJUnitに機能を追加することが可能となる。
@Rule アノテーションに関しての概要把握には以下のエントリ辺りが良さげにカバーしてそう。
- junit/doc at master ? KentBeck/junit ? GitHub(リリースノート)
- junit/doc/ReleaseNotes4.7.txt . KentBeck/junit(リリースノート4.7)
- JUnit 4.7 : テストごとのルール
コアとなるルールは以下。
ExternalResourceルール | テスト(ファイル、ソケット、サーバー、データベース接続など)、 それを後に取り壊すことを保証する前に、外部リソースを設定する ルールの基本クラス(例:TemporaryFolder) |
└─TemporaryFolderルール | テスト前にファイルを作成、テスト後に削除 |
Verifierクラス | オブジェクトが意図しない状態で終了した場合、テストを失敗させる |
└─ErrorCollectorルール | 1つのテストメソッド内で複数のエラーを収集 |
TestWatcherルール | メソッドの実行中、イベントにロジックを追加する |
└─TestNameルール | メソッドの実行中、テストの名前を利用出来る |
Timeoutルール | 設定時間を超えた場合、テストを失敗させる |
ExpectedExceptionルール | スローされる例外に対し柔軟なアサーションを行う |
TemporaryFolderルール
- ExternalResourceのサブクラス。テスト前にファイルを作成、テスト後に削除。
サンプルコード:
import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.rules.TestName; public class TemporaryFolderExample { @Rule public TemporaryFolder temporaryFolderRule = new TemporaryFolder(); @Rule public TestName testNameRule = new TestName(); @Test public void folderTest() throws IOException { System.out.println(testNameRule.getMethodName() + "() start."); File file = temporaryFolderRule.newFile("junitbc.txt"); System.out.println(file.getPath()); //temporaryFolderRule.delete(); │ //temporaryFolderRule.create(); ↓:IOException occurs! File folder = temporaryFolderRule.newFolder("junitbc2012"); System.out.println(folder.getPath()); System.out.println(testNameRule.getMethodName() + "() end."); } }
- 実行結果(Windows):
folderTest() start. C:\DOCUME~1\XXX~1.SHI\LOCALS~1\Temp\junit3577141403479062617\junitbc.txt C:\DOCUME~1\XXX~1.SHI\LOCALS~1\Temp\junit3577141403479062617\junitbc2012 folderTest() end.
- 実行経過(メソッド終了前。この状態から、メソッド完了に併せて一時ファイル・フォルダは削除される)
ErrorCollectorルール
『読んで字の通り、例外を集めて保持するクラスです。例外をaddして、最後のverifyでaddされたエラーの個数などでチェックをかけます。』
チェックを掛ける場合、addされたエラーの個数は確認出来るものなのかな?見た感じ個数を取得するメソッドとかは無さそうな感じなのだが…
@Rule public ErrorCollector collector= new ErrorCollector(); @Test public void example() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); collector.checkThat(getResult(), not(containsString("ERROR!"))); // all lines will run, and then a combined failure logged at the end. //【全ての行は実行されます。そして最後に失敗(情報)が連結され(て表示され)ます】 }
また、こちらのブログエントリにあるように
org.junit.rules.ErrorCollector は、addErrorメソッドにてテストで発生したエラーを集めておけます。 checkThatメソッドでは、テスト値の検証を行います。これらのメソッドでは問題が検知されてもテストが中断されず、 テストメソッド内の処理を最後まで実行することができます。
『失敗となっても最後まで実行され、終わったらそこまでの失敗を全て吐きだす』以外の機能は無い、という認識で良いのかな?先述のJavadoc APIにも
The ErrorCollector rule allows execution of a test to continue after the first problem is found (for example, to collect _all_ the incorrect rows in a table, and report them all at once):
とあるように、想定外の情報を一挙に出したい場合、という用途に使うべし、という事なのだろうか。一応そういう認識で先に進もう。
※なお、Verifierクラスについてはこの辺のエントリが詳しいかと。日本語のエントリがあるのは助かる。ひとまずこんな感じで使うんだな〜位の認識で留めておこうと思う。
TestNameルール
メソッドの実行中、テストの名前を利用出来る。
- サンプルコード:
package jp.shinyaa31.junitbc; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; public class TestNameExample { @Rule public TestName testNamerule = new TestName(); @Test public void checkAssertThat() { System.out.println(testNamerule.getMethodName()); } @Test public void 日本語メソッド名の確認() { System.out.println(testNamerule.getMethodName()); } }
- 実行結果:
checkAssertThat 日本語メソッド名の確認
Timeoutルール
時間内に終わらなかった場合、失敗とするルール。
public class TimeoutRuleExample { @Rule public TestRule timeoutRule = new Timeout(1000); @Test public void timeoutTest() { while(true) { System.out.println(System.currentTimeMillis()); } } }
実行結果:
java.lang.Exception: test timed out after 1000 milliseconds at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:260) : :
ExpectedExceptionルール
例外を期待するルール。
例外の種類の他にも、メッセージに含まれる文字列で判定を行う事も出来るようです。
@Rule public ExpectedException thrown = ExpectedException.none(); @Test public void throwsNothing() { // no exception expected, none thrown: passes. } @Test public void throwsNullPointerException() { thrown.expect(NullPointerException.class); throw new NullPointerException(); } @Test public void throwsNullPointerExceptionWithMessage() { thrown.expect(NullPointerException.class); thrown.expectMessage("happened?"); thrown.expectMessage(startsWith("What")); throw new NullPointerException("What happened?"); } @Test public void throwsNullPointerExceptionWithMessage2nd() { thrown.expect(NullPointerException.class); thrown.expectMessage("きのこ"); thrown.expectMessage(startsWith("この先")); throw new NullPointerException("この先生きのこるための"); }
@ClassRule アノテーション
backpaper0さんのエントリにあるように、"@BeforeClassと@AfterClassの代替となるアノテーション"となるのがこの@ClassRule。基本メソッド単位のRuleがClassRuleとしてクラス単位に範囲を広げただけ、という認識なので個別にソース写経は行わず、内容把握に留めておく事にしようと思います。(若干手抜き)