在JUnit 5中是否有更好的方法断言方法抛出异常?

目前,我必须使用@Rule来验证我的测试是否抛出异常,但这不适用于我希望多个方法在测试中抛出异常的情况。


当前回答

TL;DR:如果您使用的是JUnit 5.8.0+版本,您可以使用assertThrowsExactly()而不是assertThrows()来匹配确切的异常类型。

assertThrowsExactly(FileNotFoundException.class, () -> service.blah());

您可以使用assertThrows(),但是使用assertThrows,即使抛出的异常是子类型,您的断言也将通过。

这是因为,JUnit 5通过调用Class.isInstance(..)来检查异常类型,即使抛出的异常是子类型,Class.isInstance(..)也会返回true。

解决方法是在Class上断言:

Throwable throwable =  assertThrows(Throwable.class, () -> {
    service.readFile("sampleFile.txt");
});
assertEquals(FileNotFoundException.class, throwable.getClass());

其他回答

TL;DR:如果您使用的是JUnit 5.8.0+版本,您可以使用assertThrowsExactly()而不是assertThrows()来匹配确切的异常类型。

assertThrowsExactly(FileNotFoundException.class, () -> service.blah());

您可以使用assertThrows(),但是使用assertThrows,即使抛出的异常是子类型,您的断言也将通过。

这是因为,JUnit 5通过调用Class.isInstance(..)来检查异常类型,即使抛出的异常是子类型,Class.isInstance(..)也会返回true。

解决方法是在Class上断言:

Throwable throwable =  assertThrows(Throwable.class, () -> {
    service.readFile("sampleFile.txt");
});
assertEquals(FileNotFoundException.class, throwable.getClass());

我的解决方案:

    protected <T extends Throwable> void assertExpectedException(ThrowingRunnable methodExpectedToFail, Class<T> expectedThrowableClass,
        String expectedMessage) {
    T exception = assertThrows(expectedThrowableClass, methodExpectedToFail);
    assertEquals(expectedMessage, exception.getMessage());
}

你可以这样称呼它:

    assertExpectedException(() -> {
        carService.findById(id);
    }, IllegalArgumentException.class, "invalid id");

现在Junit5提供了一种断言异常的方法

您可以测试常规异常和定制异常

一般的异常场景:

ExpectGeneralException.java

public void validateParameters(Integer param ) {
    if (param == null) {
        throw new NullPointerException("Null parameters are not allowed");
    }
}

ExpectGeneralExceptionTest.java

@Test
@DisplayName("Test assert NullPointerException")
void testGeneralException(TestInfo testInfo) {
    final ExpectGeneralException generalEx = new ExpectGeneralException();

     NullPointerException exception = assertThrows(NullPointerException.class, () -> {
            generalEx.validateParameters(null);
        });
    assertEquals("Null parameters are not allowed", exception.getMessage());
}

您可以在这里找到一个测试CustomException的示例:断言异常代码示例

ExpectCustomException.java

public String constructErrorMessage(String... args) throws InvalidParameterCountException {
    if(args.length!=3) {
        throw new InvalidParameterCountException("Invalid parametercount: expected=3, passed="+args.length);
    }else {
        String message = "";
        for(String arg: args) {
            message += arg;
        }
        return message;
    }
}

ExpectCustomExceptionTest.java

@Test
@DisplayName("Test assert exception")
void testCustomException(TestInfo testInfo) {
    final ExpectCustomException expectEx = new ExpectCustomException();

     InvalidParameterCountException exception = assertThrows(InvalidParameterCountException.class, () -> {
            expectEx.constructErrorMessage("sample ","error");
        });
    assertEquals("Invalid parametercount: expected=3, passed=2", exception.getMessage());
}

实际上,我认为这个特殊例子的文档中有一个错误。预期的方法是expectThrows

public static void assertThrows(
public static <T extends Throwable> T expectThrows(

您可以使用assertThrows()。我的例子取自文档http://junit.org/junit5/docs/current/user-guide/

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

....

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