jUnit4: expecting an unexpected (Exceptions)

While testing the apps, we should never forget about verifying the unhappy paths – the Exceptions.
Below are a few ways to do it in jUnit4.

Example 1. 

Simply, use the existing annotation @Test with predefined value expected=ClassName.class.

@Test(expected = RuntimeException.class)
public void shouldThrowAnRuntimeException() throws Exception {
    // when
    throw new RuntimeException("Some message");
}

Although this is the simplest way to validate an exception in our test case, it is also the less expressive i.e. we cannot validate the message that is carried with the exception.

Example 2.

Lack of expressiveness in previous example is handled in jUnit4 by @Rule annotation (see below).

First we need to define the rule as a field in our test class:

@Rule
public ExpectedException thrown = ExpectedException.none();

..and than we can use it in test methods:

@Test
public void throwsExceptionWithSpecificType() {
    // then
    thrown.expect(RuntimeException.class);
    thrown.expectMessage("There was some problem with parameters");
    thrown.expectCause(Matchers.allOf(
            Matchers.instanceOf(NullPointerException.class),
            Matchers.hasProperty("message",
                    Matchers.equalTo("[importantParameter] should not be null"))
            )
    );

    // given
    String importantParameter = null;

    // when
    try {
        Objects.requireNonNull(importantParameter, "[importantParameter] should not be null");
    } catch (NullPointerException e) {
        throw new RuntimeException("There was some problem with parameters", e);
    }
}

As you can see in the above code sample, first we need to define the expected behavior, which might look a bit odd at first. Next, we prepare and execute the code that is under the test.
The clear benefit of using this approach is the possibility to verify the message type, its content, the root cause and its message as well. Moreover, all of these checks can be expressed with hamcrest matchers, which I find very beneficial.

Example 3.

Of course, we can forget about jUnit and all of these annotations that it offers to us and use some 3rd party library. One that I find very useful, expressive and simple to use is presented below:

@Test
public void shouldThrowNullPointerException() throws Exception {
    // given
    String importantParameter = null;

    // when and then
    org.assertj.core.api.Assertions.assertThatThrownBy(
            () -> {
                try {
                    Objects.requireNonNull(importantParameter, "[importantParameter] should not be null");
                } catch (NullPointerException e) {
                    throw new RuntimeException("There was some problem with parameters", e);
                }
            })
            .isInstanceOf(RuntimeException.class)
            .hasMessage("There was some problem with parameters")
            .hasCauseInstanceOf(NullPointerException.class);
    // Note: cannot check the cause message
    //      .hasMessage("[importantParameter] should not be null");
}

I like this approach the most. Though, the API does not offer the possibility to validate the message of the inner exception (the one that actually may cause the issue), it has a nice feature of:
– wrapping the code under the test, and
– defining the expected behavior with the builder style pattern.

Advertisements