When writing unit tests for a Java API there may be circumstances where you want to perform more detailed validation of an exception. I.e. more than is offered by the @t
For JUNIT 3.x
public void test(){
boolean thrown = false;
try{
mightThrowEx();
} catch ( Surprise expected ){
thrown = true;
assertEquals( "message", expected.getMessage());
}
assertTrue(thrown );
}
For JUnit 5 it is much easier:
@Test
void testAppleIsSweetAndRed() throws Exception {
IllegalArgumentException ex = assertThrows(
IllegalArgumentException.class,
() -> testClass.appleIsSweetAndRed("orange", "red", "sweet"));
assertEquals("this is the exception message", ex.getMessage());
assertEquals(NullPointerException.class, ex.getCause().getClass());
}
By returning the exception object itself, assertThrows()
allows you to test every aspect regarding your thrown exceptions.
As provided in your answer, it's a good approach. In addition to this:
You could wrap the function expectException
into a new Annotation, called ExpectedException
.
An annotated method would look like this:
@Test
@ExpectedException(class=WrapperException.class, message="Exception Message", causeException)
public void testAnExceptionWrappingFunction() {
//whatever you test
}
This way would be more readable, but it's exactly the same approach.
Another reason is: I like Annotations :)
The following helper method (adapted from this blog post) does the trick:
/**
* Run a test body expecting an exception of the
* given class and with the given message.
*
* @param test To be executed and is expected to throw the exception.
* @param expectedException The type of the expected exception.
* @param expectedMessage If not null, should be the message of the expected exception.
* @param expectedCause If not null, should be the same as the cause of the received exception.
*/
public static void expectException(
Runnable test,
Class<? extends Throwable> expectedException,
String expectedMessage,
Throwable expectedCause) {
try {
test.run();
}
catch (Exception ex) {
assertSame(expectedException, ex.getClass());
if (expectedMessage != null) {
assertEquals(expectedMessage, ex.getMessage());
}
if (expectedCause != null) {
assertSame(expectedCause, ex.getCause());
}
return;
}
fail("Didn't find expected exception of type " + expectedException.getName());
}
The test code can then invoke this as follows:
TestHelper.expectException(
new Runnable() {
public void run() {
classInstanceBeingTested.methodThatThrows();
}
},
WrapperException.class,
"Exception Message",
causeException
);