JUnit4でprivateメソッドをテストする

Test

JUnit 4でプライベートメソッドのテストをしようとしてハマったのでメモ。

簡単には、Javaのリフレクションを使ってプライベートメソッドを実行します。クラスからメソッドを取得して、それをinvokeする感じです。

サンプル

以下の様なクラスを使用します。

public class Weekly {

    private boolean inRangeOfWeekly(int weekly) {
        return Calendar.SUNDAY <= weekly && weekly <= Calendar.SATURDAY;
    }

    private String getNameByWeekly(int weekly) {
        switch (weekly) {
            case Calendar.SUNDAY:
                return "日曜日";
            case Calendar.MONDAY:
                return "月曜日";
            case Calendar.TUESDAY:
                return "火曜日";
            case Calendar.WEDNESDAY:
                return "水曜日";
            case Calendar.THURSDAY:
                return "木曜日";
            case Calendar.FRIDAY:
                return "金曜日";
            case Calendar.SATURDAY:
                return "土曜日";
            default:
                throw new IllegalArgumentException("不正なweekly: " + weekly + " です");
        }
    }
}

テスト

WeeklyクラスからメソッドをMethodクラスとして取得し、プライベートメソッドをアクセス可能にしてから、実行します。

public class WeeklyTest {


    Weekly mWeekly;

    @Before
    public void setUp() throws Exception {
        mWeekly = new Weekly();
    }

    @After
    public void tearDown() throws Exception {
        mWeekly = null;
    }

    @Test
    public void testIsRangeOfWeekly() throws Exception {
        // method取得
        Method method = Weekly.class.getDeclaredMethod("inRangeOfWeekly", int.class);
        // privateメソッドを実行可能に
        method.setAccessible(true);

        Assert.assertThat((Boolean) method.invoke(mWeekly, 1), CoreMatchers.is(true));
        Assert.assertThat((Boolean) method.invoke(mWeekly, 20), CoreMatchers.is(false));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testGetNameByWeeklyWithException() throws Exception {
        // method取得
        Method method = Weekly.class.getDeclaredMethod("getNameByWeekly", int.class);
        // privateメソッドを実行可能に
        method.setAccessible(true);
        try {
            method.invoke(mWeekly, 300);
        } catch (InvocationTargetException e) { // invokeで発生したExceptionはInvocationTargetExceptionでWrapされる
            throw (Exception) e.getCause();
        }
    }
}

注意点

invokeを使用してExceptionをThrowする場合は、InvocationTargetExceptionにラップされて発生するので、それをcatchしてCauseを再度Throwします。

まとめ

こんな感じでprivateメソッドもテストできます。

※アイキャッチ画像にはフリーフォントのようじょふぉんとを使わせて頂いております。