予想される例外を使用してメソッドをテストしています。また、例外がスローされた後に(モックされたオブジェクトで)クリーンアップコードが呼び出されたことを確認する必要がありますが、その確認は無視されているようです。これがコードです。 Junit ExpectedException
Rule
を使用して、予期される例外を確認しています。
@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
にどのメソッドを入れても、テストは成功しますが、これは私が望んでいるものではありません。
それが起こっている理由は何ですか?
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());
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(...));
}
私はまだこれを試していませんが、Jeff Bowmanの優れた答えに加えて、expectedExceptionルールをtry ... finallyコンストラクトで使用し、verifyステートメントをfinallyブロックに配置することもできます。
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(...));
}
}