The HIRO Says

If you smell what The HIRO is cooking!!!

【slim3】jUnit4.x系の使い方

前回も触れましたが、slim3 のテストでは jUnit 4.7 を使用しています。


jUnit は、4.x 系になってから、仕様がアノテーションベースに変更になっています。
前回はうまくまとめられなかったので、今回は jUnit 4.x 系の使い方を簡単にまとめてみようと思います。


ポイント

4.x 系の場合、大きく2つのポイントがあります。

  1. TestCase を継承しない
  2. アノテーションを使用する
TestCaseを継承しない

4.x 系では、@Test アノテーション(後述)をテストメソッドにつければテスト対象として認識されます。
そのため、3.x 系までのように org.junit.TestCase クラスを継承する必要はありません。
※特別にクラスを継承することは可能。Service のテストで触れる予定。


但し、TestCase クラスを継承しなくなると、assertXxx() などのメソッドを修飾子なしで呼び出すことができなくなり面倒です。
(例えば、assertXxx() は本来 org.junit.Assert クラスのメソッドで、TestCase がラップしているため、修飾子なしで呼び出すことができます。)
そのため 4.x 系では、以下のように、該当の static インポートを追加することになります。

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;


slim3 の環境構築手順で、org.hamcrest.CoreMatchers などをコンテンツアシストに追加したのは、
上記に円滑に対応するためです。(たぶん)


アノテーションを使用する

4.x 系では、以下のアノテーションをメソッドに付記して、テストコードを作成していきます。

アノテーション 意味 FQN
@Test テスト対象メソッド org.junit.Test
@Test(expected=例外クラス.class) 指定した例外が発生したらOK org.junit.Test
@Test(timeout=ミリ秒) 指定時間をオーバーしたらNG org.junit.Test
@Ignore("comment") 一時的にテスト対象外とする(@Testにつける) org.junit.Ignore
@Before これまでの setUp() に相当するもの org.junit.Before
@After これまでの tearDown() に相当するもの org.junit.After
@BeforeClass 全テスト実行前に1回だけ実施される前処理 org.junit.BeforeClass
@AfterClass 全テスト実行後に1回だけ実施される後処理 org.junit.AfterClass

プログラム例と実行結果例

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

// モデル Order のテストという前提
public class OrderTest {

    // テスト対象
    private Order target = null;

    @Before
    public void before() {
        System.out.println("setUp");
        this.target = new Order();
    }

    @After
    public void after() {
        System.out.println("tearDown");
        this.target = null;
    }

    // 全テストで1回だけ実施
    @BeforeClass
    public static void beforeAll() {
        System.out.println("beforeAll");
    }

    // 全テストで1回だけ実施
    @AfterClass
    public static void afterAll() {
        System.out.println("afterAll");
    }


// ↓テストケース
    // 通常のケース
    @Test
    public void test() throws Exception {
        assertThat(this.target, is(notNullValue()));
    }


    // 例外発生の検証
    @Test(expected=NullPointerException.class)
    public void expectError() {
        this.target = null;
        this.target.toString();
    }


    // 無視の検証
    @Test(expected=IllegalArgumentException.class)
    @Ignore("test")
    public void unexpectError() {
        this.target = null;
        this.target.toString();
        System.out.println("This case will be ignored");
    }


    // 想定時間内に処理が終了することの検証
    @Test(timeout=1000)
    public void testTimeout() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("sleeping...");
        }
    }
}
実行結果
beforeAll
setUp
tearDown
setUp
tearDown
setUp
sleeping...
 〜(略)
tearDown
afterAll

気がついた点

(1)Before/After/BeforeClass/AfterClass は public
public にしないと、以下のエラーが発生します。
java.lang.Exception: Method xxx() should be public


(2)Before/After/BeforeClass/AfterClass は void
void にしないと、以下のエラーが発生します。
java.lang.Exception: Method xxx() should be void


(3)BeforeClass/AfterClass は static
static にしないと、以下のエラーが発生します。
java.lang.Exception: Method xxx() should be static


(4)Before/After/BeforeClass/AfterClass のメソッド名は自由
@Before で after() とか命名できますが…混乱するので避けましょうw


(5)Ignore は Before/After/BeforeClass/AfterClass には効かない
@Ignore は、テストメソッドにのみ効果があるようです。


(6)Before/After/BeforeClass/AfterClass は複数指定可能
実行順序が問題。
後で調べます。