问题
Is there a better way to assert that a method throws an exception in JUnit 5?
Currently, I have to use an @Rule in order to verify that my test throws an exception, but this doesn\'t work for the cases where I expect multiple methods to throw exceptions in my test.
回答1:
You can use assertThrows(), which allows you to test multiple exceptions within the same test. With support for lambdas in Java 8, this is the canonical way to test for exceptions in JUnit.
Per the JUnit docs:
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"));
}
回答2:
In Java 8 and JUnit 5 (Jupiter) we can assert for exceptions as follows.
Using org.junit.jupiter.api.Assertions.assertThrows
public static < T extends Throwable > T assertThrows(Class< T > expectedType, Executable executable)
Asserts that execution of the supplied executable throws an exception of the expectedType and returns the exception.
If no exception is thrown, or if an exception of a different type is thrown, this method will fail.
If you do not want to perform additional checks on the exception instance, simply ignore the return value.
@Test
public void itShouldThrowNullPointerExceptionWhenBlahBlah() {
assertThrows(NullPointerException.class,
()->{
//do whatever you want to do here
//ex : objectName.thisMethodShoulThrowNullPointerExceptionForNullParameter(null);
});
}
That approach will use the Functional Interface Executable
in org.junit.jupiter.api
.
Refer :
- http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
- http://junit.org/junit5/docs/5.0.0-M2/api/org/junit/jupiter/api/Executable.html
- http://junit.org/junit5/docs/5.0.0-M4/api/org/junit/jupiter/api/Assertions.html#assertThrows-java.lang.Class-org.junit.jupiter.api.function.Executable-
回答3:
They've changed it in JUnit 5 (expected: InvalidArgumentException, actual: invoked method) and code looks like this one:
@Test
public void wrongInput() {
Throwable exception = assertThrows(InvalidArgumentException.class,
()->{objectName.yourMethod("WRONG");} );
}
回答4:
Now Junit5 provides a way to assert the exceptions
You can test both general exceptions and customized exceptions
A general exception scenario:
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());
}
You can find a sample to test CustomException here : assert exception code sample
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());
}
回答5:
You can use assertThrows()
. My example is taken from the docs 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());
}
回答6:
I think this is an even simpler example
List<String> emptyList = new ArrayList<>();
Optional<String> opt2 = emptyList.stream().findFirst();
assertThrows(NoSuchElementException.class, () -> opt2.get());
Calling get()
on an optional containing an empty ArrayList
will throw a NoSuchElementException
. assertThrows
declares the expected exception and provides a lambda supplier (takes no arguments and returns a value).
Thanks to @prime for his answer which I hopefully elaborated on.
回答7:
Actually I think there is a error in the documentation for this particular example. The method that is intended is expectThrows
public static void assertThrows(
public static <T extends Throwable> T expectThrows(
回答8:
Here is an easy way.
@Test
void exceptionTest() {
try{
model.someMethod("invalidInput");
fail("Exception Expected!");
}
catch(SpecificException e){
assertTrue(true);
}
catch(Exception e){
fail("wrong exception thrown");
}
}
It only succeeds when the Exception you expect is thrown.
回答9:
There are 3 ways to assert a certain exception in Junit. Let's write the unit test cases for it.
1. try-catch idiom This idiom is one of the most popular ones because it was used already in JUnit 3. This approach is a common pattern. The test will fail when no exception is thrown and the exception itself is verified in a catch clause.
@Test
public void convertIntoUpperCase_withInvalidInput_tryCatchIdiom() {
try {
exceptionHandling.convertIntoUpperCase("");
fail("It should throw IllegalArgumentException");
} catch (IllegalArgumentException e) {
Assertions.assertThat(e)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Empty value is passed.");
}
}
2. @Test expected annotation In this approach, we specify the expected exception in @Test as below @Test(expected = IllegalArgumentException.class)
When the exception wasn’t thrown you will get the following message: java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException
With this approach, you need to be careful though. Sometimes it is tempting to expect general Exception, RuntimeException or even a Throwable. And this is considered as a bad practice because your code may throw an exception in other places than you actually expected and your test will still pass!
One of the drawback of this approach is you can’t assert for the exception message.
@Test(expected = IllegalArgumentException.class)
public void convertIntoUpperCase_withInvalidInput_testExpected() {
exceptionHandling.convertIntoUpperCase("");
}
3. Junit @Rule The same example can be created using ExceptedException rule. The rule must be a public field marked with @Rule annotation.
@Test
public void convertIntoUpperCase_withInvalidInput_ExpectedExceptionRule() {
exception.expect(IllegalArgumentException.class);
exception.expectMessage("Empty value is passed.");
exceptionHandling.convertIntoUpperCase("");
}
I find the above code more readable hence I prefer to use this approach.
When the exception isn’t thrown you will get the following message: java.lang.AssertionError: Expected test to throw (an instance of java.lang.IllegalArgumentException and exception with the message “Empty value is passed.”). Pretty nice.
But not all exceptions I check with the above approach. Sometimes I need to check only the type of the exception thrown and then I use @Test annotation.
来源:https://stackoverflow.com/questions/40268446/junit-5-how-to-assert-an-exception-is-thrown