JUnit4.7 の新機能 Rules とは〜その2

先日は、@Ruleの使い方として、VerifierとTemporaryFolderについて見てきました。本日はその他のRuleについて見ていきます。

Timeoutルール

org.junit.rules.Timeout は、全てのテストメソッドに、同じタイムアウト時間を設定します。コンストラクタにてミリセカンドでタイムアウトを設定します。

    @Rule public MethodRule globalTimeout= new Timeout(20);

    @Test public void testInfiniteLoop() {
    for (;;) {
        ・・・
    }

TestNameルール

org.junit.rules.TestNameルールは、テストメソッドの名前を取得可能にするルールです。以下の例では、testA というテストメソッド名が評価されています。まあ、何に使うの?という所はありますが・

    @Rule public TestName name= new TestName();
    @Test public void testA() {
        assertEquals(testA, name.getMethodName());
    }

ExpectedException

org.junit.rules.ExpectedExceptionは、例外の種類やメッセージについての検証を柔軟に追加することができます。以下の例では、メソッド内で発生した例外がNullPointerExceptionであり、メッセージとして happened が含まれているかを検証しています。

    @Rule public ExpectedException thrown = ExpectedException.none();

    @Test public void throwsNullPointerExceptionWithMessage() {
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened");
        throw new NullPointerException("What happened?");
    }

ErrorCollectorルール

org.junit.rules.ErrorCollector は、addErrorメソッドにてテストで発生したエラーを集めておけます。checkThatメソッドでは、テスト値の検証を行います。これらのメソッドでは問題が検知されてもテストが中断されず、テストメソッド内の処理を最後まで実行することができます。

    @Rule public ErrorCollector collector= new ErrorCollector();

    @Test public void test() {
        collector.addError(new Throwable("hoge"));
        collector.checkThat(getSize(), not(0));
        ・・・
    }
}

ExternalResource ルール

org.junit.rules.ExternalResource は、各テストメソッドの前後に、外部リソースなどの操作を行うことができるルールです。

    Server myServer= new Server();

    @Rule
    public ExternalResource resource= new ExternalResource() {
        @Override protected void before() throws Throwable {
            myServer.connect();
        };

        @Override protected void after() {
            myServer.disconnect();
        };
    };

    @Test
    public void testFoo() {・・}

TestWatchman

org.junit.rules.TestWatchmanは、テストメソッドに手を加えずに、テストに振る舞いを追加できます。以下の例では、各テストメソッドの成功時と失敗時に行う処理を追加しています。

    private static String log;
    @Rule public MethodRule watchman= new TestWatchman() {
        @Override public void failed(Throwable e, FrameworkMethod method) {
            log += method.getName() + " " + e.getClass().getSimpleName();
        }

        @Override public void succeeded(FrameworkMethod method) {
            log += method.getName() + " " + "success";
        }
    };

この他に、テストの開始時と終了時に処理を行うための以下のメソッドも用意されています。

    public void starting(FrameworkMethod method)
    public void finished(FrameworkMethod method)

まとめ

JUnitに組み込みのRuleは、@Beforeや@Test(expected=・・)などといった、アノテーションとして実現可能なものが多く提供されています。Ruleとして同じ様な機能が提供されている理由は、これらの機能をRuleによって拡張することができる点にあります。
Ruleの価値は、組み込みのRuleを拡張して、用途に合った振る舞いをテストに簡単に追加できる点にあります。@Beforeや@Test(expected=・・)などのアノテーションで実現できる場合は、わざわざRuleを使用することは無いと思います。