web-dev-qa-db-ja.com

例外Junit 4.10の後のMockitoの検証

予想される例外を使用してメソッドをテストしています。また、例外がスローされた後に(モックされたオブジェクトで)クリーンアップコードが呼び出されたことを確認する必要がありますが、その確認は無視されているようです。これがコードです。 Junit ExpectedExceptionRuleを使用して、予期される例外を確認しています。

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

@Test
public void testExpectedException()
{
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   expectedEx.expect(MyException.class);
   expectedEx.expectMessage("My exception message.");
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

verifyは完全に無視されているようです。 verifyにどのメソッドを入れても、テストは成功しますが、これは私が望んでいるものではありません。

それが起こっている理由は何ですか?

62
Eqbal

ExpectedExceptionは、 JUnit @Rule を介してtry-catchブロックで テストメソッド全体をラップする によって機能します。コードが例外をスローすると、スタックを最も近いtry/catchに移動します。これは、ExpectedExceptionインスタンスにあります(予想される例外であることを確認します)。

Javaでは、キャッチされていない例外がメソッドで発生した場合、制御はそのメソッドの後半のステートメントに戻りません。ここでも同じルールが適用されます:例外の後、テスト内のステートメントに制御が戻ることはありません。

技術的には、最終ブロックに検証を入れることもできますが、それは 悪い習慣 になる傾向があります。 EDIT:テスト中のシステムは、予期しない例外をスローするか、まったく例外をスローしない場合があり、有用なエラーメッセージとトレースを提供します。ただし、その失敗によりfinallyブロックで検証またはアサーションが失敗する場合、Javaは、予期しない例外または予期しない成功に関するメッセージではなく、それを表示します。特に、エラーはエラーの根本原因に続くコード行から発生し、誤ってそのコードが成功したことを意味するため、デバッグが困難になる可能性があります。

メソッドごとに例外後の状態を本当に確認する必要がある場合は、いつでもこのイディオムに戻すことができます。

@Test
public void testExpectedException()
{
  MockedObject mockObj = mock(MockedObj.class);
  MySubject subject = new MySubject(mockedObj);
  try {
    subject.someMethodThrowingException();
    fail("Expected MyException.");
  } catch (MyException expected) {
    assertEquals("My exception message.", expected.getMessage());
  }
  verify(mockObj).someCleanup(eq(...));
}

Update:Java 8のラムダ式で、機能ブロック呼び出しをtryブロックでラップすることができます 簡潔で十分便利です 。この構文のサポートは、多くの標準テストライブラリに取り入れられると思います。

assertThrows(MyException.class,
    () -> systemUnderTest.throwingMethod());
71
Jeff Bowman

catch-exception を使用したよりエレガントなソリューション

@Test
public void testExpectedException()
{
    MockedObject mockObj = mock(MockedObject.class);
    MySubject subject = new MySubject(mockObj);

    when(subject).someMethodThrowingException();

    then(caughtException())
            .isInstanceOf(MyException.class)
            .hasMessage("My exception message.");

    verify(mockObj).someCleanup(eq(...));
}
5
MariuszS

私はまだこれを試していませんが、Jeff Bowmanの優れた答えに加えて、expectedExceptionルールをtry ... finallyコンストラクトで使用し、verifyステートメントをfinallyブロックに配置することもできます。

4
Kevin Welker

UTで例外がスローされると、その下のすべてのコードは無視されます。

@Test(expected = Exception.class)
public void testExpectedException() {
   MockedObject mockObj = mock(MockedObj.class);
   MySubject subject = new MySubject(mockedObj);
   subject.doSomething(); // If this line results in an exception then all the code below this will be ignored.
   subject.someMethodThrowingException();
   verify(mockObj).
       someCleanup(eq(...));
}

これに対抗し、行われたすべての呼び出しを確認するには、try with finallyを使用できます。

@Test(expected = Exception.class)
    public void testExpectedException() {
          MockedObject mockObj = mock(MockedObj.class);
          MySubject subject = new MySubject(mockedObj);
          try {
               subject.someMethodThrowingException(); 
          } finally {
             verify(mockObj).
             someCleanup(eq(...));
          }
} 
2
singh30