Blog

Java 8 で例外を投げていることをテストしたい

特定の条件で例外を投げるコードを書いたときに、それをテストしたい。なんてときがある。 このクラスの例外があがってるってことだけ判断できればいいんだけどな、ぐらいのゆるい条件のとき。

そんな時は以下のようなユーティリティを定義すればいい。

public static void assertThrows(Class<? extends Exception> exceptionClass, Code code) {
    boolean thrown = false;
    try {
        code.run();
    } catch (Exception ex) {
        System.out.println(ex);
        assertTrue(exceptionClass.isInstance(ex));
        thrown = true;
    }
    assertTrue(thrown);
}

@FunctionalInterface
public interface Code {
    public void run() throws Exception;
}

利用法は以下のようになる。

@Test
public void testDelete() throws SQLException, HanaException {
    Member m1 = new Member().setEmail("[email protected]").insert(conn);
    Assert.assertNotNull(m1);
    Member m2 = new Member().setEmail("[email protected]").insert(conn);
    Assert.assertNotNull(m2);
    Follow follow = new Follow()
            .setFromMemberId(m1.getId())
            .setToMemberId(m2.getId())
            .insert(conn);

    assertThrows(HanaNoPrimaryKeyException.class, () -> {
        follow.delete(conn);
    });
}

以前の Java ではこのような簡便な書き方はできなかったが、Java8 では lambda expression を利用して、LL に近い表現が可能になっている。


と、ここまで書いたところで、実は JUnit でできるんじゃないかと思って調べてみたところ、以下のような解決策があることがわかった。


Spring Framework の場合は以下のようにできるようだ http://docs.spring.io/spring/docs/2.5.x/api/org/springframework/test/AssertThrows.html

 public class FooTest {
    public void testSomeBusinessLogicBadArgumentPath() {
        new AssertThrows(IllegalArgumentException.class) {
            public void test() {
                new Foo().someBusinessLogic(null);
            }
        }.runTest();
    }
 }

これは素晴らしい。これならなにも問題がない。ただ、lambda を使ってる場合よりちょっと長いけど、 このサンプルは spring の古いバージョンのドキュメントを見ているのでしょうがないのかもしれない。 Spring の新しいバージョンのドキュメントを調べたいところだが、めんどくさいので無理だった。


また、StackOverflow によれば以下のように JUnit の機能で書けるようだ。 http://stackoverflow.com/questions/156503/how-do-you-assert-that-a-certain-exception-is-thrown-in-junit-4-tests

@Test(expected=IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

しかしこれは、テストの条件を Annotation に出してしまっていて、非常にわかりづらい。 しらない人が見てもこの annotation のパラメータがテスト条件だとは気づかないだろう。