Help us understand the problem. What is going on with this article?

JUnit4での例外テストを楽チンにする!

More than 1 year has passed since last update.

2018/6/13追記
jUtaime は JUnit5 の登場によりその使命を終えました。
長らくご愛顧ありがとうございました m(_ _)m
みなさま JUnit5 を使いましょう。

What's an issue ?

JUnitでの例外検証コードをもっとスッキリ書きたいっ!
そう思っているのはきっと私だけではないはず・・・

before

例えば Person#setAge(int) なんていうよくあるメソッドを作ったとして、次の4点を検証したい。

  • setAge(-1) は NG → IllegalArgumentException をスロー
  • setAge(0) は OK
  • setAge(200) は OK : 超長寿社会の到来に備えて。
  • setAge(201) は NG → IllegalArgumentException をスロー、例外メッセージあり

通常のJUnit4ではこんな感じになると思います。

JUnit4での通常の例外テスト
public class PersonTest {

    private Person person = new Person("John");

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

    @Test(expected = IllegalArgumentException.class)
    public void testSetAge1() {
        person.setAge(-1);
    }

    @Test
    public void testSetAge2() {
        try  {
            person.setAge(0);
            person.setAge(200);  // 200歳まではOKとする。
        } catch(RuntimeException e) {
            fail();
        }
    }

    @Test
    public void testSetAge3() {
        thrown.expect(IllegalArgumentException.class);
        thrown.expectMessage("Is he a zombie!?");

        person.setAge(201);
    }
}

なーんでたったこれっぽっちの検証で、こんなにもっさりしたコードを書かなきゃいけないんですかね! ぷんぷん

after

これで良いじゃん...

こう書きたい
public class PersonTest {

    @Test
    public void testSetAge() {
        Person p = new Person("John");
        assertThat(of(() -> p.setAge(-1)),  raise(IllegalArgumentException.class));
        assertThat(of(() -> p.setAge(0)),   raiseNothing());
        assertThat(of(() -> p.setAge(200)), raiseNothing());
        assertThat(of(() -> p.setAge(201)), raise(IllegalArgumentException.class, "Is he a zombie!?"));
    }
}

so that

というわけで、afterを実現するライブラリを作りました。
nmby/jetaime · GitHub
nmby/jUtaime · GitHub (2015/7/26 移動しました。)

usage

  • 例外をスローしうる検証対象の処理を、Testee.of() の中に記述します。
  • 期待する例外の型やメッセージ、原因(cause)の型などを、RaiseMatchers.raise() などで指定します。
assertThat(Testee.of(SomeClass::someMethodShouldFail),
        RaiseMatchers.raise(SomeException.class, "expected message"));

TesteeRaiseMatchers は static インポートしておくとよいでしょう。
次のような色々な書き方が可能です。

assertThat(of(() -> Integer.valueOf("abc")), raiseExact(NumberFormatException.class));
assertThat(of(() -> Integer.valueOf("123")), raiseNothing());
assertThat(of(() -> { Object o = null; o.toString(); }), raise(RuntimeException.class));
assertThat(of(obj::dbOperation), rootCause(IOException.class));

また、他の Matcher と組み合わせて使用することもできます。
次の例では、hamcrest.org が提供する anyOfallOfnot と組み合わせて使用しています。

// NullPointerException または IllegalArgumentException がスローされることを検証
assertThat(of(() -> obj.someOperation(null)),
        anyOf(raise(NullPointerException.class), raise(IllegalArgumentException.class)));

// NullPointerException 以外の何らかの実行時例外を原因として上位例外がスローされることを検証
assertThat(of(() -> obj.someOperation(param)),
        allOf(raise(WrappingException.class, "expected message"),
                rootCause(RuntimeException.class),
                not(rootCause(NullPointerException.class))));

allOf() の代わりに、次の連結スタイルで記述することも可能です。

// NullPointerException 以外の何らかの実行時例外を原因として上位例外がスローされることを検証
assertThat(of(() -> obj.someOperation(param)),
        raise(WrappingException.class, "expected message")
                .rootCause(RuntimeException.class)
                .not(rootCause(NullPointerException.class)));

詳しくは javadoc を見てね♪

なお、eclipseで使えばちゃんとレポートも表示されます。
JUnitTraceView.png

dependencies

  • java 8 以上が必要です。
  • hamcrest-core も必要ですが、JUnit4を使ってるならすでに入ってるはずです。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした