例えば、
public class Sample { /** * do something. * * @param i * a positive value. * @throws IllegalArgumentException * if i is a negative value. */ public static void doSomething(int i) { if (i < 0) throw new IllegalArgumentException("negative arg: " + i); ... } }
というコードをテストする場合を考えます。
さて、『doSomethingはiが負数の場合にIllegalArgumentExceptionをスローします。』は
Javadocで明示的に宣言している公開API仕様ですから、それを満たすことを確かめる単体テストを行うべきです。
で、それをどう書くか。
JUnitのアノテーションを使う
やり方だとこうなる。
@Test(expected = IllegalArgumentException.class) public void testDoSomething() { Sample.doSomething(-1); }
try-catchを使う
やり方だとこうなる。
@Test public void testDoSomething() { try { Sample.doSomething(-1); fail(); } catch (final IllegalArgumentException e) { assertTrue(e.getMessage().matches(".*nagative.+-1")); } }
さて、一見したところ
アノテーションを使う方法のほうが、スッキリ書けてヨサゲな感じです。
だがしかし。
アノテーションを使う方法だと、『とにかくIllegalArgumentExceptionがスローされれば良い』
ということになるため、doSomethingの本体部分で何かの理由によりIllegalArgumentExceptionが
スローされてしまっても、テストには成功したように見えてしまいます。
例えば、こんな感じ。(かなり恣意的ですが)
class Sample { /** ... */ static void doSomething(int i) { if (i < -1) // ←間違ってる throw new IllegalArgumentException("negative arg: " + i); new LinkedHashMap(i); // ←ココでIllegalArgumentException発生 } }
こんな風に間違っているコードに対して先ほどのテストを実行しても、
成功してしまうわけです。
というわけで、僕は常にtry-catchを使う方法を採用しています。
今日のテーマは大したことないように見えますが、
油断すると意外と差が付くこともあるよ、ということでした。はい。
ちなみに
org.junit.ExpectedExceptionなるクラスがあるそうです。
http://kentbeck.github.com/junit/javadoc/latest/org/junit/rules/ExpectedException.html
使い方はこんな感じ。
// These tests all pass. public static class HasExpectedException { @Rule public ExpectedException thrown= new ExpectedException(); @Test public void throwsNothing() { // no exception expected, none thrown: passes. } @Test public void throwsNullPointerException() { thrown.expect(NullPointerException.class); throw new NullPointerException(); } @Test public void throwsNullPointerExceptionWithMessage() { thrown.expect(NullPointerException.class); thrown.expectMessage("happened?"); thrown.expectMessage(startsWith("What")); throw new NullPointerException("What happened?"); } }
。。。どうやら、EasyMockのように予め『例外がスローされるよ!』と宣言しておくスタイルのようです。
うーむ、なんかイマイチ分かり辛い。
というわけで、僕は使ってません。