JUnit 4テストで特定の例外がスローされるとどのように主張しますか?


2000

JUnit4を慣用的に使用して、一部のコードが例外をスローすることをテストするにはどうすればよいですか?

私は確かにこのようなことをすることができますが:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

これらの種類の状況では、アノテーションやAssert.xyzなど、JUnitに比べてそれほど扱いづらくなく、はるかに強力なものがあることを思い出します。


21
他のアプローチの問題ですが、例外がスローされると、テストが必ず終了します。一方、org.mockito.Mockito.verify例外がスローされる前に、特定のことが発生したこと(ロガーサービスが正しいパラメーターで呼び出されたなど)を確認するために、引き続きさまざまなパラメーターを使用して呼び出したいことがよくあります。
ZeroOne 2013年


6
@ZeroOne-そのため、私は2つの異なるテストを行います。1つは例外用で、もう1つはモックとの相互作用を検証するためのものです。
tddmonkey 2014

JUnit 5でこれを行う方法があります。以下で私の回答を更新しました。
Dilini Rajapaksha 2017

回答:


2363

これは、JUnitのバージョンと、使用するライブラリをアサートするものによって異なります。

の元の答えJUnit <= 4.12は:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

ただし、https://stackoverflow.com/a/31826781/2986984にはJUnit 4.12以下のオプションがあります。

参照 :


66
このコードの一部は、コードのどこかにのみ例外があり、このようなブランケットではない場合は機能しません。
Oh Chin Boon、2011年

4
@skaffmanこれはorg.junit.experimental.theories.Theoriesによって実行されるorg.junit.experimental.theories.Theoriesでは機能しません
Artem Oboturov '27

74
例外は、テスト中のユニット内だけでなく、テスト内のどこにでも存在する可能性があるため、ロイオセロベは、このような例外テストをアートユニットテストで推奨していません。
Kevin Wittek、2015年

21
@ Kiview / Roy Osheroveに同意しません。私の見解では、テストは実装のためではなく、動作のためのものであるべきです。特定のメソッドがエラーをスローする可能性があることをテストすることで、テストを実装に直接結び付けます。上記の方法でテストすると、より価値のあるテストが得られると私は主張します。追加する警告は、この場合はカスタム例外をテストするため、本当に必要な例外が発生していることがわかるということです。
nickbdyer 2016年

6
どちらでもない。クラスの動作をテストしたい。重要なのは、そこにないものを取得しようとすると、例外が発生することです。データ構造がArrayList応答するということget()は関係ありません。将来、プリミティブ配列に移動することを選択した場合、このテスト実装を変更する必要があります。テストがクラスの動作に集中できるように、データ構造を非表示にする必要があります。
nickbdyer 2016年

1317

編集: JUnit 5とJUnit 4.13がリリースされたので、Assertions.assertThrows() (JUnit 5の場合)およびAssert.assertThrows()(JUnit 4.13の場合)を使用するのが最良のオプションです。詳細については、他の回答を参照してください。

JUnit 5に移行していないが、JUnit 4.7を使用できる場合は、次のExpectedExceptionルールを使用できます。

public class FooTest {
  @Rule
  public final ExpectedException exception = ExpectedException.none();

  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    exception.expect(IndexOutOfBoundsException.class);
    foo.doStuff();
  }
}

これは@Test(expected=IndexOutOfBoundsException.class)IndexOutOfBoundsException前にスローされた場合にテストが失敗するためよりもはるかに優れていますfoo.doStuff()

詳細はこちらの記事をご覧ください


14
@skaffman-これを正しく理解している場合、exception.expectはクラス全体ではなく1つのテスト内でのみ適用されているようです。
bacar

5
スローされると予想される例外がチェック例外である場合、スローを追加するか、この状況を別の方法でテストまたはキャッチする必要がありますか?
Mohammad Jafar Mashhadi 2013年

5
@MartinTrummer例外がスローされてメソッドが終了するため、foo.doStuff()の後にコードを実行する必要はありません。予期された例外(finallyでリソースを閉じることを除く)の後にコードがあると、例外がスローされた場合は実行されないため、いずれにしても役に立ちません。
Jason Thompson

9
これが最良のアプローチです。スキャフマンのソリューションと比較して、ここには2つの利点があります。まず、ExpectedExceptionクラスには例外のメッセージを照合する方法、または例外のクラスに依存する独自のマッチャーを作成する方法さえあります。次に、例外をスローすると予想されるコード行の直前に期待値を設定できます。つまり、間違ったコード行が例外をスローすると、テストが失敗します。一方、スカフマンのソリューションでそれを行う方法はありません。
Dawood ibnカリーム2014

5
@MJafarMashスローするはずの例外がチェックされている場合は、その例外をテストメソッドのthrows句に追加します。例外が特定のテストケースでトリガーされない場合でも、チェックされた例外をスローするように宣言されたメソッドをテストするときはいつでも同じことを行います。
NamshubWriter

472

予想される例外の使用には注意が必要です。メソッドがその例外をスローしただけであり、テスト内の特定のコード行はスローされなかったからです

このようなメソッドは通常非常に単純ですが、より複雑なテストには以下を使用する方がよいため、これをパラメーター検証のテストに使用する傾向があります。

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

判断を適用します。


95
多分私は古い学校ですが、私はまだこれを好みます。また、例外自体をテストする場所も提供します。特定の値のゲッターで例外が発生したり、メッセージで特定の値を探したりする場合があります(たとえば、「認識されないコード 'xyz'のメッセージで「xyz」を探します) ")。
ロドニージッツェ

3
NamshubWriterのアプローチは、両方の長所を提供すると思います。
Eddie

4
ExpectedExceptionを使用すると、メソッドごとにN exception.expectを呼び出して、このexception.expect(IndexOutOfBoundsException.class);のようにテストできます。foo.doStuff1(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff2(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff3();
user1154664

10
@ user1154664実際にはできません。ExpectedExceptionを使用すると、1つのメソッドが例外をスローすることをテストできます。これは、そのメソッドが呼び出されると、予期された例外をスローしたため、テストの実行が停止するためです。
NamshubWriter 2014

2
最初の文は真実ではありません。を使用するExpectedException場合、通常は、例外をスローすると予想される行の直前に期待値を設定します。このように、前の行が例外をスローした場合、ルールはトリガーされず、テストは失敗します。
Dawood ibnカリーム2014

213

以前に回答したように、JUnitの例外を処理する方法はたくさんあります。しかし、Java 8にはもう1つ、ラムダ式の使用があります。ラムダ式を使用すると、次のような構文を実現できます。

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrownは、ラムダ式、メソッド参照、またはコンストラクター参照でインスタンスを作成できる機能インターフェースを受け入れます。そのインターフェースを受け入れるassertThrownは、例外を予期し、例外を処理する準備ができています。

これは比較的単純ですが強力な手法です。

この手法を説明するこのブログ投稿をご覧くださいhttp : //blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

ソースコードはここにあります:https : //github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

開示:私はブログとプロジェクトの作成者です。


2
このソリューションは好きですが、Mavenリポジトリからダウンロードできますか?
セルウィン2015年

@Airduster Mavenで利用できるこのアイデアの1つの実装はstefanbirkner.github.io/valladoです
NamshubWriter

6
@CristianoFontes JUnit 4.13では、このAPIのより単純なバージョンが予定されています。github.com/junit-team/junit/commit/…を
NamshubWriter

@RafalBorowiecは技術的にnew DummyService()::someMethodはですがMethodHandle、このアプローチはラムダ式でも同様に機能します。
アンディ

@NamshubWriter、JUnitの4.13は、JUnitの5の賛成で放棄されたようです:stackoverflow.com/questions/156503/...
ヴァジム・

154

junitでは、例外をテストする4つの方法があります。

junit5.x

  • junit5.xの場合assertThrows、次のように使用できます

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }

junit4.x

  • junit4.xの場合、テスト注釈のオプションの 'expected'属性を使用します

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
  • junit4.xの場合、ExpectedExceptionルールを使用します

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
  • junit 3フレームワークで広く使用されている従来のtry / catch方法を使用することもできます

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
  • そう

    • junit 5が好きなら、最初のものが好き
    • 2番目の方法は、例外のタイプのみをテストする場合に使用されます
    • 最初と最後の2つは、さらにテスト例外メッセージが必要な場合に使用されます
    • junit 3を使用する場合は、4番目が優先されます
  • 詳細については、このドキュメントと詳細についてはjunit5ユーザーガイドを参照してください。


6
私にとってはこれが最良の答えです、それはすべての方法を非常に明確にカバーしています、ありがとう!個人的には、読みやすさのためにJunit4でも3番目のオプションを使い続けています。空のcatchブロックを回避するために、Throwableをキャッチしてeのタイプをアサートすることもできます
Nicolas Cornette

ExpectedExceptionを使用してチェック済み例外を期待することは可能ですか?
miuser

それはすべて、上位3つの回答の蓄積です。IMO、何も新しいものを追加していない場合、この回答は投稿されるべきではありません。担当者に(よくある質問)答えるだけです。かなり役に立たない。
ポールサムソタ

確実に、から派生した任意の型をTrowableメソッドに渡すことができますExpectedException.expect署名をご覧ください。@miuser
ウォルシュ

116

tl; dr

  • JDK8以降:AssertJまたはカスタムラムダを使用して、例外的な動作をアサートします

  • pre-JDK8:私は古い良いものをお勧めしますtry- catchブロック。(ブロックの前にアサーションを追加することを忘れないでくださいfail()catch

JUnit 4またはJUnit 5に関係なく。

長い話

自分で作成することが可能であることを自分でやる try - catchブロックまたはJUnitのツール(使用@Test(expected = ...)または@Rule ExpectedExceptionJUnitのルール機能)。

しかし、これらの方法はそれほどエレガントではなく、他のツールと読みやすさをうまく組み合わせていません。さらに、JUnitツールにはいくつかの落とし穴があります。

  1. try- catchあなたがテストした行動の周りにブロックを書き込み、catchブロックでアサーションを書き、それは罰金が、このスタイルは、テストの読み取り流れを遮断することを多くの発見かもしれする必要がブロック。また、ブロックのAssert.fail最後にを書き込む必要がありtryます。そうしないと、テストでアサーションの一方が欠落する可能性があります。PMDfindbugsSonarはこのような問題を見つけます。

  2. この@Test(expected = ...)機能は興味深いです。少ないコードで記述でき、このテストを記述すると、コーディングエラーが発生しにくくなると考えられます。しかし、このアプローチはいくつかの領域で欠けています。

    • 原因やメッセージなど、例外に関する追加の事項をテストでチェックする必要がある場合(適切な例外メッセージは非常に重要であり、正確な例外タイプを用意するだけでは不十分な場合があります)。
    • また、メソッドに期待が置かれているため、テストされたコードの記述方法によっては、テストコードの間違った部分が例外をスローし、誤検知テストが発生する可能性があります。また、PMDfindbugsまたはSonarこのようなコードのヒントを提供します。

      @Test(expected = WantedException.class)
      public void call2_should_throw_a_WantedException__not_call1() {
          // init tested
          tested.call1(); // may throw a WantedException
      
          // call to be actually tested
          tested.call2(); // the call that is supposed to raise an exception
      }
  3. このExpectedExceptionルールは、以前の警告を修正するための試みでもありますが、期待スタイルを使用するため、使用するのが少し面倒に感じられます。EasyMockユーザーは、このスタイルをよく知っています。一部の人にとっては便利かもしれませんが、ビヘイビア駆動開発(BDD)またはアレンジアサート(AAA)の原則に従う場合、ExpectedExceptionルールはそれらの記述スタイルに適合しません。それとは別に@Test、期待を置く場所によっては、方法と同じ問題が発生する可能性があります。

    @Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }

    予想される例外がテストステートメントの前に配置されていても、テストがBDDまたはAAAに従っている場合は、読み取りフローが中断されます。

    また、の作者のJUnitに関するこのコメントの問題も参照してくださいExpectedExceptionJUnit 4.13-beta-2では、このメカニズムは廃止されています。

    プルリクエスト#1519:ExpectedExceptionの廃止

    Assert.assertThrowsメソッドは、例外を検証するためのより良い方法を提供します。さらに、TestWatcherなどの他のルールでExpectedExceptionを使用すると、ルールの順序が重要になるため、エラーが発生しやすくなります。

したがって、これらの上記のオプションにはすべての注意事項があり、明らかにコーダーエラーの影響を受けません。

  1. この回答を作成した後で気づいたプロジェクトには、有望に見えるキャッチ例外があります。

    プロジェクトの説明にあるように、コーダーは例外をキャッチする滑らかなコード行を記述し、後者のアサーションにこの例外を提供できます。また、HamcrestAssertJなどの任意のアサーションライブラリを使用できます。

    ホームページから抜粋した簡単な例:

    // given: an empty list
    List myList = new ArrayList();
    
    // when: we try to get the first element of the list
    when(myList).get(1);
    
    // then: we expect an IndexOutOfBoundsException
    then(caughtException())
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessage("Index: 1, Size: 0") 
            .hasNoCause();

    コードが非常に簡単であることがわかるように、特定の行で例外をキャッチしthenます。APIはAssertJ APIを使用するエイリアスです(を使用するのと同様assertThat(ex).hasNoCause()...)。ある時点で、プロジェクトはAssertJの祖先であるFEST-Assertに依存しました編集:プロジェクトがJava 8 Lambdasサポートを作成しているようです。

    現在、このライブラリには2つの欠点があります。

    • この記事の執筆時点では、このライブラリはMockito 1.xに基づいていると言うことは注目に値します。これは、テストされたオブジェクトのモックを舞台裏で作成するためです。Mockitoはまだ更新されていないため、このライブラリは最終クラスまたは最終メソッドで機能しません。また、現在のバージョンのMockito 2に基づいていたとしても、グローバルモックメーカー(inline-mock-maker)を宣言する必要があります。このモックメーカーには、通常のモックメーカーとは異なる欠点があるためです。

    • さらに別のテスト依存関係が必要です。

    ライブラリがラムダをサポートすると、これらの問題は適用されなくなります。ただし、機能はAssertJツールセットによって複製されます。

    catch-exceptionツールを使用したくない場合は、すべてを考慮に入れて、少なくともJDK7までのtry- catchブロックの古い良い方法をお勧めします。また、JDK 8ユーザーの場合は、AssertJを使用することをお勧めします。AssertJは、例外をアサートするだけではありません。

  2. JDK8を使用すると、ラムダはテストシーンに入り、例外的な動作を主張する興味深い方法であることが証明されました。AssertJが更新され、例外的な動作を表明するための優れた流暢なAPIが提供されます。

    そしてAssertJ使ったサンプルテスト:

    @Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
  3. JUnit 5のほぼ完全な書き直しにより、アサーションは少し改善れました。適切に例外をアサートするためのすぐに使える方法として、アサーションは興味深いものになるかもしれません。しかし、実際にはアサーションAPIはまだ少し貧弱で、外には何もありませんassertThrows

    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }

    お気づきのとおり、assertEqualsがまだ返さvoidれているため、AssertJのようなアサーションのチェーンは許可されていません。

    あなたが持つ名前の衝突を覚えていればまたMatcherAssert、と同じ衝突を満たすために準備することAssertions

今日(2017-03-03)AssertJの使いやすさ、発見可能なAPI、開発の速いペース、そして事実上のテスト依存性は、テストフレームワーク(JUnitかどうか)、以前のJDKは代わりに依存する必要がありますtry-catch不格好に感じてもブロックします。

この回答は、同じ可視性を持たない別の質問からコピーされました。私は同じ著者です。


1
org.junit.jupiter:junit-jupiter-engine:5.0.0-RC2依存関係を(既存のjunit:junit:4.12に加えて)追加してassertThrowsを使用できるようにすることは、おそらく推奨されるソリューションではありませんが、私にとっての問題。
anre

私はExpectedExceptionルールを使用するのが好きですが、AAAでうまく機能しないことにいつも悩まされていました。あなたはさまざまなアプローチすべてを説明する優れた記事を書いており、AssertJを試してみるように私を励ましてくれました:-)ありがとう!
Pim Hazebroek

@PimHazebroekありがとう。AssertJ APIは非常に豊富です。私の意見では、JUnitが箱から出して提案するものの方が良いです。
ブライス

64

JUnit 5とJUnit 4.13がリリースされたので、Assertions.assertThrows() (JUnit 5の場合)およびAssert.assertThrows()(JUnit 4.13の場合)を使用するのが最良のオプションです。Junit 5ユーザーガイドを参照してください。

例外がスローされたことを確認し、Truthを使用して例外メッセージをアサートする例を次に示します。

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

他の回答のアプローチに対する利点は次のとおりです。

  1. JUnitに組み込まれています
  2. ラムダ内のコードが例外をスローしない場合は便利な例外メッセージが表示され、別の例外がスローされる場合はスタックトレースが表示されます
  3. 簡潔な
  4. テストがArrange-Act-Assertに従うことを許可します
  5. 例外をスローする予定のコードを正確に示すことができます
  6. 予想される例外をthrows条項に記載する必要はありません
  7. 選択したアサーションフレームワークを使用して、キャッチされた例外に関するアサーションを作成できます

同様のメソッドがorg.junit AssertJUnit 4.13に追加されます。


このアプローチはクリーンですが、「Act」の部分を「assertThrow」にラップする必要があるため、これによってテストが「Arrange-Act-Assert」をどのように実行できるかわかりません。
時計じかけ

@Clockworkラムダは「行為」です。Arrange-Act-Assertの目標は、コードをクリーンでシンプルなものにすることです(したがって、理解と保守が容易です)。あなたが述べたように、このアプローチはクリーンです。
NamshubWriter

テストの最後の "assert"部分でスローと例外をアサートできることをまだ望んでいました。このアプローチでは、最初にキャッチするために、最初のアサートで行為をラップする必要があります。
時計じかけ

アサーションを行うには、すべてのテストでより多くのコードが必要になります。これはコードが多くなり、エラーが発生しやすくなります。
NamshubWriter

43

これについてはどうですか:非常に一般的な例外をキャッチし、それがキャッチブロックの外に出ていることを確認してから、例外のクラスが想定どおりであることをアサートします。このアサートは、a)例外のタイプが間違っている場合(たとえば、代わりにnullポインターを取得した場合)、b)例外がスローされなかった場合に失敗します。

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}

3
また、テストが失敗する日が来たときに、どのような例外exがテスト結果にあるかはわかりません。
jontejj 2013年

これは、最後にアサートする方法を変更することで少し改善できます。assertEquals(ExpectedException.class, e.getClass())テストが失敗すると、期待値と実際の値が表示されます。
サイファー、2018年

37

BDDスタイルのソリューション:JUnit 4 + キャッチ例外 + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

    then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

}

依存関係

eu.codearte.catch-exception:catch-exception:2.0

36

使用AssertJの JUnitを一緒に使用することができアサーションを、:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

それはより良いでしょう@Test(expected=IndexOutOfBoundsException.class)、それは例外投げたテストで期待されるラインを保証しているため、あなたは、このようなメッセージとして例外の詳細を、確認でき、より簡単に:

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Maven / Gradleの手順はこちら。


最も簡潔な方法であり、誰もそれを高く評価していません。奇妙です。assertJライブラリに問題があるのは、assertThatだけで、junitの名前と競合します。assertJ throwbyの詳細:JUnit:Java 8とAssertJ 3.0.0による例外のテスト〜Codeleak.pl
ycomp

@ycompええと、それは非常に古い質問に対する新しい答えなので、スコアの違いは疑わしいものです。
ウェストン2016年

Java 8とAssertJを使用できる場合、これがおそらく最良のソリューションです。
Pierre Henry

@ycompこの名前の競合は仕様によるものではないかと思います。したがって、AssertJライブラリでは、JUnit assertThatは使用せず、常にAssertJ を使用することを強くお勧めします。また、JUnitメソッドは「通常の」タイプのみを返しますが、AssertJメソッドはAbstractAssertサブクラスを返します...上記のようにメソッドの文字列を許可します(またはこれに関する専門用語は何でも...)
マイクげっ歯類

@westonは実際にはAssertJ 2.0.0であなたのテクニックを使用しました。アップグレードしない言い訳は間違いありませんが、あなたが知りたいかもしれませんが。
マイクげっ歯類

33

同じ問題を解決するために、小さなプロジェクトを設定しました:http : //code.google.com/p/catch-exception/

この小さなヘルパーを使用して、あなたは書くでしょう

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

これは、JUnit 4.7のExpectedExceptionルールよりも簡潔です。skaffmanが提供するソリューションと比較して、例外を予期するコード行を指定できます。これがお役に立てば幸いです。


私もこのようなことをすることを考えましたが、最終的にExpectedExceptionの真の力は、予期される例外を指定できるだけでなく、予期される原因や予期されるメッセージなど、例外の特定のプロパティも指定できることです。
Jason Thompson

私の推測では、このソリューションにはモックと同じ欠点がいくつかありますか?例えば、場合foofinalプロキシすることはできませんので、それは失敗しますかfoo
Tom

Tom、doStuff()がインターフェースの一部である場合、プロキシーアプローチが機能します。そうしないと、このアプローチは失敗します。
rwitzel

31

更新: JUnit5では、例外テストが改善されていますassertThrows

次の例は、Junit 5ユーザーガイドからのものです

 @Test
void exceptionTesting() {
    Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
    {
        throw new IllegalArgumentException("a message");
    });
    assertEquals("a message", exception.getMessage());
}

JUnit 4を使用した元の回答。

例外がスローされたことをテストする方法はいくつかあります。以下のオプションについても投稿しました。JUnitを使用して優れた単体テストを作成する方法

expectedパラメータを設定します@Test(expected = FileNotFoundException.class)

@Test(expected = FileNotFoundException.class) 
public void testReadFile() { 
    myClass.readFile("test.txt");
}

使用する try catch

public void testReadFile() { 
    try {
        myClass.readFile("test.txt");
        fail("Expected a FileNotFoundException to be thrown");
    } catch (FileNotFoundException e) {
        assertThat(e.getMessage(), is("The file test.txt does not exist!"));
    }

}

ExpectedExceptionルールによるテスト。

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

@Test
public void testReadFile() throws FileNotFoundException {

    thrown.expect(FileNotFoundException.class);
    thrown.expectMessage(startsWith("The file test.txt"));
    myClass.readFile("test.txt");
}

例外テストの詳細については、JUnit4 wikiの例外テストbad.robot-Expecting Exceptions JUnit Ruleを参照してください


22

これを行うこともできます:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}

12
JUnitテストでAssert.fail()assert、アサーションが有効になっていない環境でテストが実行される場合に備えて、ではなくを使用することをお勧めします。
NamshubWriter 2015年

14

私見、JUnitで例外をチェックする最良の方法は、try / catch / fail / assertパターンです。

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

assertTrue何人かの人々のために少し強いかもしれませんので、assertThat(e.getMessage(), containsString("the message");望ましいかもしれません。



13

Mkyongブログで見つけたJunit 4の最も柔軟でエレガントな答え。アノテーションtry/catchを使用するという柔軟性があります@Rule。カスタマイズされた例外の特定の属性を読み取ることができるため、このアプローチが好きです。

package com.mkyong;

import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;

public class Exception3Test {

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

    @Test
    public void testNameNotFoundException() throws NameNotFoundException {

        //test specific type of exception
        thrown.expect(NameNotFoundException.class);

        //test message
        thrown.expectMessage(is("Name is empty!"));

        //test detail
        thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
        thrown.expect(hasProperty("errCode", is(666)));

        CustomerService cust = new CustomerService();
        cust.findByName("");

    }

}

12

ここで多くの方法を試しましたが、それらは複雑であるか、私の要件を完全に満たしていませんでした。実際、ヘルパーメソッドを非常に簡単に作成できます。

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

次のように使用します。

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

依存関係なし:mockitoは不要、powermockは不要。そして最終クラスでうまく機能します。


興味深いですが、AAA(アサートアサート)には適合しません。実際の異なるステップでアクトとアサートのステップを実行したい場合です。
bln-tom 2014年

1
@ bln-tom技術的には2つの異なるステップですが、それらはこの順序ではありません。; p
Trejkaz、2015

10

Java 8ソリューション

次のようなソリューションが必要な場合:

  • Java 8ラムダを利用
  • ないJUnitマジックに依存
  • 単一のテストメソッド内で複数の例外をチェックできます
  • テストメソッド全体の不明な行ではなく、テストメソッド内の特定の行セットによってスローされる例外をチェックします
  • スローされた実際の例外オブジェクトを生成して、さらに詳しく調べられるようにする

ここに私が書いたユーティリティ関数があります:

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

私のブログから抜粋

次のように使用します。

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}


8

私の場合、常にdbからRuntimeExceptionを取得しますが、メッセージが異なります。そして例外はそれぞれ処理する必要があります。これが私がそれをテストした方法です:

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}

1
との行の前に} catch (、挿入する必要がありますfail("no exception thrown");
Daniel Alder

6

次のように、オンとオフを切り替えることができるマッチャーを作成するだけです。

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

それを使用するには:

追加public ExpectedException exception = ExpectedException.none();してから:

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();

6

JUnit 4以降では、次のように例外をテストできます

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


これは、JUnitテストの改善に使用できる多くの機能を提供します。
以下の例が表示された場合、例外について3つのことをテストしています。

  1. スローされた例外のタイプ
  2. 例外メッセージ
  3. 例外の原因


public class MyTest {

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

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}

6

例外を返さなければならないメソッドの後にアサーション失敗を使用できます:

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}

3
2番目catchは、他の例外がスローされた場合にスタックトレースを飲み込み、有用な情報を失います
NamshubWriter

5

また何にNamShubWriterが言っている、ことを確認してください:

  • ExpectedExceptionインスタンスはパブリックです関連質問
  • ExpectedException 、たとえば@Beforeメソッドではインスタンス化されません。この投稿では、JUnitの実行順序のすべての複雑さを明確に説明しています。

ではない次の操作を行います。

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

最後に、このブログ投稿は、特定の例外がスローされたことを表明する方法を明確に示しています。


4

ライブラリをお勧めしますassertj-corejunitテストで例外を処理する

Java 8では、次のようになります。

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);

2

Java8でのJunit4ソリューションは、この関数を使用することです:

public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

使用法は次のとおりです。

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

唯一の制限はfinal、ラムダ式でオブジェクト参照を使用することです。このソリューションを使用すると、ソリューションを使用してメソッドレベルでどのように処理できるかを期待する代わりに、テストアサーションを続行できます@Test(expected = IndexOutOfBoundsException.class)


1

たとえば、以下のコードフラグメントのJunitを記述したいとします。

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

上記のコードは、発生する可能性のある不明な例外をテストするためのものであり、以下のコードは、カスタムメッセージでいくつかの例外をアサートするためのものです。

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

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}

1
    @Test(expectedException=IndexOutOfBoundsException.class) 
    public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
         doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
         try {
             foo.doStuff(); 
            } catch (IndexOutOfBoundsException e) {
                       assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                      throw e;

               }

    }

メソッドが正しい例外をスローしたかどうかを確認する別の方法を次に示します。


1

JUnitフレームワークにはassertThrows()メソッドがあります。

ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
    calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());

0

Java 8では、チェックするコードと予想される例外をパラメーターとして取るメソッドを作成できます。

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

そしてあなたのテストの中で:

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

利点:

  • ライブラリに依存しない
  • ローカライズされたチェック-より正確で、必要に応じて1つのテスト内でこのような複数のアサーションを持つことができます
  • 使いやすい
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.