web-dev-qa-db-ja.com

JUnitテストが例外をスローするのに悪い形ですか?

私はJUnitにかなり慣れていないので、例外と例外処理のベストプラクティスが何であるかを本当に知りません。

たとえば、IPAddressクラスのテストを書いているとしましょう。 addrがnullの場合、InvalidIPAddressExceptionをスローするコンストラクターIPAddress(String addr)があります。私がグーグルを回ってわかる限り、nullパラメーターのテストは次のようになります。

@Test
public void testNullParameter()
{
    try
    {
        IPAddress addr = new IPAddress(null);
        assertTrue(addr.getOctets() == null);
    }
    catch(InvalidIPAddressException e)
    {
        return;
    }

    fail("InvalidIPAddressException not thrown.");
}

この場合、例外が発生することがわかっているので、try/catchが理にかなっています。

しかし、今私がtestValidIPAddress()を書きたいなら、それをするいくつかの方法があります:

方法#1:

@Test
public void testValidIPAddress() throws InvalidIPAddressException
{
    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();

    assertTrue(octets[0] == 127);
    assertTrue(octets[1] == 0);
    assertTrue(octets[2] == 0);
    assertTrue(octets[3] == 1);
}

方法#2:

@Test
public void testValidIPAddress()
{
    try
    {
        IPAddress addr = new IPAddress("127.0.0.1");
        byte[] octets = addr.getOctets();

        assertTrue(octets[0] == 127);
        assertTrue(octets[1] == 0);
        assertTrue(octets[2] == 0);
        assertTrue(octets[3] == 1);
    }
    catch (InvalidIPAddressException e)
    {
        fail("InvalidIPAddressException: " + e.getMessage());
    }
}

JUnitに予期しない例外をスローするのが標準的な方法ですか、それとも自分で処理するだけですか?

助けてくれてありがとう。

56
Seth

実際には、古いスタイルの例外テストは、例外をスローするコードの周りにtryブロックをラップしてから、tryブロックの最後にfail()ステートメントを追加します。このようなもの:

_public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}
_

これはあなたが書いたものと大した違いはありませんが:

  1. assertTrue(addr.getOctets() == null);は役に立たない。
  2. 意図と構文はより明確なIMOであり、したがって読みやすくなっています。

それでも、これは少しいです。ただし、例外テストはJUnit 4の最大の改善点の1つであるため、ここでJUnit 4が役立ちます。JUnit4では、次のようにテストを記述できます。

_@Test (expected=InvalidIPAddressException.class) 
public void testNullParameter() throws InvalidIPAddressException {
    IPAddress addr = new IPAddress(null);
}
_

いいですね。

さて、本当の質問に関しては、例外がスローされることを期待していない場合、間違いなく方法#1に進み(冗長性が低いため)、JUnitに例外を処理させ、期待どおりにテストを失敗させます。

91
Pascal Thivent

例外を期待しないテストの場合、わざわざそれをキャッチしません。 JUnitに例外をキャッチさせ(これを確実に行います)、throwsの原因を宣言する(必要な場合)以外はまったく対応しません。

私は再注意してください。 first@expectedアノテーションを使用していない例.

@Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
  }

例外をスローするためにテストしているすべてのテストにこれを使用します。 Junit3で使用しなければならなかった同等のキャッチ/フェイルパターンよりも簡単です。

10
Brian Agnew

JUnit 4.7以降では、 ExpectedException ルールを使用する可能性があるため、使用する必要があります。このルールを使用すると、テストコードで例外をスローする必要のある呼び出されたメソッドを正確に定義できます。さらに、例外のエラーメッセージに対して文字列を簡単に一致させることができます。あなたの場合、コードは次のようになります。

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

    @Test
    public void test() {
        //working code here...
        expectedException.expect(InvalidIPAddressException.class);
        IPAddress addr = new IPAddress(null);
    }

UPDATE:彼の本では JUnitとMockitoを使用した実用的な単体テスト Tomek KaczanowskiはExpectedExceptionの使用に反対している。ユニットテストのarrange/act/assert [...]フローを中断します(代わりに Catch Exception Library を使用することを提案します)。彼の主張は理解できますが、別のサードパーティライブラリを導入したくない場合は、ルールを使用しても問題ないと思います(とにかく例外を「手動で」キャッチするよりも、ルールを使用する方が優れています)。

9
s106mo

Nullテストの場合は、これを簡単に行うことができます。

public void testNullParameter() {
    try {
            IPAddress addr = new IPAddress(null);
            fail("InvalidIPAddressException not thrown.");
    }
    catch(InvalidIPAddressException e) { }
}

例外にメッセージがある場合、必要に応じてキャッチでそのメッセージを確認することもできます。例えば。

String actual = e.getMessage();
assertEquals("Null is not a valid IP Address", actual);

有効なテストのために、例外をキャッチする必要はありません。例外がスローされ、キャッチされない場合、テストは自動的に失敗します。したがって、方法#1だけで十分です。失敗すると、スタックトレースが表示されます。

0
digiarnie

Reg:例外のテスト
「Pascal Thivent」に同意します。つまり、@Test (expected=InvalidIPAddressException.class)を使用します


Reg:testValidIPAddressのテスト

IPAddress addr = new IPAddress("127.0.0.1");
byte[] octets = addr.getOctets();

私は次のようなテストを書くでしょう

class IPAddressTests
{

    [Test]
    public void getOctets_ForAValidIPAddress_ShouldReturnCorrectOctect()
    {
         // Test code here
    }

}

ポイントは、testinputが有効なipAddressの場合です
テストは、例外として動作していることを表明するクラスのパブリックメソッド/機能に対して行う必要があります。

0
Venu b

私はあなたの質問を理解している場合、答えはどちらかです-個人的な好み。

個人的には、テストで例外をスローします。私の意見では、アサーションによって失敗するテストは、キャッチされない例外によって失敗するテストと同等です。どちらも修正が必要なものを示しています。

テストで覚えておくべき重要なことは、コードカバレッジです。

0
pstanton

一般的な方法#1が方法であり、エラーよりも失敗を呼び出す理由はありません-どちらの場合でも、テストは本質的に失敗しました。

何がうまくいかなかったのかについての良いメッセージが必要な場合、唯一の時間的方法#2は理にかなっており、ただ例外はあなたにそれを与えません。次に、キャッチと失敗は、失敗の理由をより適切に発表するのに意味があります。

0
Yishai