@Test
アノテーションを使用していくつかのJUnitテストを作成しました。私のテストメソッドがチェック済み例外をスローし、例外とともにメッセージをアサートしたい場合、JUnit @Test
アノテーションでそれを行う方法はありますか?知る限り、JUnit 4.7はこの機能を提供していませんが、将来のバージョンでは提供されますか? .NETでは、メッセージと例外クラスをアサートできることを知っています。 Javaの世界で同様の機能を探しています。
これは私が欲しいものです:
@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}
次のように @Rule
アノテーションを ExpectedException
とともに使用できます。
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
expectedEx.expect(RuntimeException.class);
expectedEx.expectMessage("Employee ID is null");
// do something that should throw the exception...
}
ExpectedException
ドキュメントの例は(現在)間違っていることに注意してください-パブリックコンストラクターがないため、ExpectedException.none()
を使用する必要があります。
@Rule
回答が好きです。ただし、何らかの理由でルールを使用したくない場合。 3番目のオプションがあります。
@Test (expected = RuntimeException.class)
public void myTestMethod()
{
try
{
//Run exception throwing operation here
}
catch(RuntimeException re)
{
String message = "Employee ID is null";
assertEquals(message, re.getMessage());
throw re;
}
fail("Employee Id Null exception did not throw!");
}
@Test(expected=SomeException.class)
を使用する必要がありますか?例外の実際のメッセージをアサートする必要がある場合、これが私たちのすることです。
@Test
public void myTestMethod()
{
try
{
final Integer employeeId = null;
new Employee(employeeId);
fail("Should have thrown SomeException but did not!");
}
catch( final SomeException e )
{
final String msg = "Employee ID is null";
assertEquals(msg, e.getMessage());
}
}
JUnit 4.13(1回リリース)では、次のことができます。
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
...
@Test
void exceptionTesting() {
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> { throw new IllegalArgumentException("a message"); }
);
assertEquals("a message", exception.getMessage());
}
これは JUnit 5 でも機能しますが、インポートが異なります:
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
...
実際、最適な使用法はtry/catchを使用することです。どうして?例外が予想される場所を制御できるためです。
この例を考えてみましょう:
@Test (expected = RuntimeException.class)
public void someTest() {
// test preparation
// actual test
}
ある日コードが変更され、テストの準備がRuntimeExceptionをスローした場合はどうなりますか?その場合、実際のテストはテストされず、例外をスローしなくてもテストはパスします。
そのため、注釈に依存するよりもtry/catchを使用する方がはるかに優れています。
レイストームには良い答えがありました。私もルールの大ファンではありません。読みやすさと使いやすさを支援する次のユーティリティクラスを作成することを除いて、私は似たようなことをします。
このユーティリティクラスを追加します。
import org.junit.Assert;
public abstract class ExpectedRuntimeExceptionAsserter {
private String expectedExceptionMessage;
public ExpectedRuntimeExceptionAsserter(String expectedExceptionMessage) {
this.expectedExceptionMessage = expectedExceptionMessage;
}
public final void run(){
try{
expectException();
Assert.fail(String.format("Expected a RuntimeException '%s'", expectedExceptionMessage));
} catch (RuntimeException e){
Assert.assertEquals("RuntimeException caught, but unexpected message", expectedExceptionMessage, e.getMessage());
}
}
protected abstract void expectException();
}
次に、単体テストに必要なのは、次のコードのみです。
@Test
public void verifyAnonymousUserCantAccessPrivilegedResourceTest(){
new ExpectedRuntimeExceptionAsserter("anonymous user can't access privileged resource"){
@Override
protected void expectException() {
throw new RuntimeException("anonymous user can't access privileged resource");
}
}.run(); //passes test; expected exception is caught, and this @Test returns normally as "Passed"
}
私はuser64141の答えが好きですが、もっと一般化できることがわかりました。私の見解は次のとおりです。
public abstract class ExpectedThrowableAsserter implements Runnable {
private final Class<? extends Throwable> throwableClass;
private final String expectedExceptionMessage;
protected ExpectedThrowableAsserter(Class<? extends Throwable> throwableClass, String expectedExceptionMessage) {
this.throwableClass = throwableClass;
this.expectedExceptionMessage = expectedExceptionMessage;
}
public final void run() {
try {
expectException();
} catch (Throwable e) {
assertTrue(String.format("Caught unexpected %s", e.getClass().getSimpleName()), throwableClass.isInstance(e));
assertEquals(String.format("%s caught, but unexpected message", throwableClass.getSimpleName()), expectedExceptionMessage, e.getMessage());
return;
}
fail(String.format("Expected %s, but no exception was thrown.", throwableClass.getSimpleName()));
}
protected abstract void expectException();
}
Tryブロック内に「fail」ステートメントを残すと、関連するアサーション例外がキャッチされることに注意してください。 catchステートメント内でreturnを使用すると、これを防ぐことができます。
@Ruleを使用している場合、例外セットはTestクラスのすべてのテストメソッドに適用されます。
@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
//test code
}
catch-exception ライブラリをインポートして使用します。 ExpectedException
ルールやtry-catch
よりもずっときれいです。
ドキュメントの例:
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;
// given: an empty list
List myList = new ArrayList();
// when: we try to get the first element of the list
catchException(myList).get(1);
// then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0"
assertThat(caughtException(),
allOf(
instanceOf(IndexOutOfBoundsException.class),
hasMessage("Index: 1, Size: 0"),
hasNoCause()
)
);