JUnit 5:如何断言抛出异常?

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

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

506945 次浏览

你可以使用assertThrows(),它允许你在同一个测试中测试多个异常。由于Java 8支持lambdas,这是JUnit中测试异常的规范方法。

根据JUnit文档:

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


@Test
void exceptionTesting() {
MyException thrown = assertThrows(
MyException.class,
() -> myObject.doThing(),
"Expected doThing() to throw, but it didn't"
);


assertTrue(thrown.getMessage().contains("Stuff"));
}

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

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

他们在JUnit 5中改变了它(预期:InvalidArgumentException,实际:invoked method),代码看起来像这样:

@Test
public void wrongInput() {
Throwable exception = assertThrows(InvalidArgumentException.class,
()->{objectName.yourMethod("WRONG");} );
}

你可以使用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());
}
在Java 8和JUnit 5 (Jupiter)中,我们可以这样断言异常。 使用org.junit.jupiter.api.Assertions.assertThrows < / p >

public static <T extends Throwable > T assertThrows(Class<T > expectedType, 可执行可执行文件)< /强> < / p >

断言所提供的可执行文件的执行将抛出expectedType的异常并返回该异常。

如果没有抛出异常,或者抛出了不同类型的异常,则此方法将失败。

如果您不想对异常实例执行额外的检查,只需忽略返回值。

@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
assertThrows(NullPointerException.class,
()->{
//do whatever you want to do here
//ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
});
}

该方法将使用org.junit.jupiter.api中的函数接口Executable

参考:

现在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());
}

我认为这是一个更简单的例子

List<String> emptyList = new ArrayList<>();
Optional<String> opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());

在包含空ArrayList的可选对象上调用get()将抛出NoSuchElementExceptionassertThrows声明了预期的异常并提供了一个lambda提供者(不接受参数并返回一个值)。

感谢@prime的回答,我希望能详细说明。

这里有一个简单的方法。

@Test
void exceptionTest() {


try{
model.someMethod("invalidInput");
fail("Exception Expected!");
}
catch(SpecificException e){


assertTrue(true);
}
catch(Exception e){
fail("wrong exception thrown");
}


}

它只有在抛出您期望的异常时才会成功。

更简单的一行代码。使用Java 8和JUnit 5,本例不需要lambda表达式或花括号

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


@Test
void exceptionTesting() {


assertThrows(MyException.class, myStackObject::doStackAction, "custom message if assertion fails...");


// note, no parenthesis on doStackAction ex ::pop NOT ::pop()
}

TL;博士:如果你使用的是JUnit 5.8.0+版本,你可以使用assertThrowsExactly()代替assertThrows()来匹配准确的异常类型。

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

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

这是因为,JUnit 5通过调用Class.isIntance(..)来检查异常类型,即使抛出的异常是子类型,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");