34
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-07-03

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を使ってるならすでに入ってるはずです。
34
30
5

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?