Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Exception Testing in Java and AssertJ

DZone's Guide to

Exception Testing in Java and AssertJ

JUnit's assertions might be handy, but AssertJ might have a leg up against the competition, allowing you to catch exceptions without breaking your flow.

· Java Zone
Free Resource

Just released, a free O’Reilly book on Reactive Microsystems: The Evolution of Microservices at Scale. Brought to you in partnership with Lightbend.

As long as I have been writing tests and seeing others write them, Java exceptions have been a problem for many programmers, including those more experienced ones. When an exception is being thrown, it stops the execution of the code going up to the top of the execution stack, or until it's handled. In unit test environments, this usually means that the execution of a given test is stopped, and whatever code runs after the exception is not going to be executed. This means that the most obvious way to verify the exception is to do it in a catch block:

// easy exception handling
@Test
public void test_and_validate_exception() throws Exception {
    // given
    MyService testObject = new MyService();

    // when-then
    try {
        testObject.throwAnException();
	    fail("Expected exception not thrown");
    } catch (Exception ex) {
        assertTrue(ex instanceof NullPointerException);
    }
}


The code does exactly what it's supposed to do — verifies that some NullPointerException is being thrown from the method. It's valid, but I still don't like it. It introduces some logic to the validation, something that should be avoided in tests. In a simple example like the one above, it's usually not a problem, but with larger number and more complex tests, it can obfuscate the purpose.

One way to handle that is to add parameters to the @Test annotation. This is pretty straightforward:

// this is for JUnit 4+, in TestNG it's expectedExceptions
@Test(expected = NullPointerException.class)
public void test_and_validate_exception() throws Exception {
    // given
    MyService testObject = new MyService();

    // when-then
    testObject.throwAnException();
}


Thankfully, later versions of JUnit allow you to use the rules mechanism. They provide wrappers over the tests, allowing you to modify the execution of the particular test. You can implement your own rules, but JUnit provides a couple of them pre-implemented. One of them is ExpectedException, which, as you would have probably guessed, expects an exception. With that, the next step in our example looks like this:

// needs to be public
@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void test_and_validate_exception() throws Exception {
    // given
    MyService testObject = new MyService();

    // expect
    thrown.expect(NullPointerException.class);

    // when
    testObject.throwAnException();
}


Now, all the logic of the test is explicitly stated in the test, and with ExpectedException, you can check the class, the message, and the underlying cause. The only thing that bothers me is having to state the requirements for the exception before the tested method. This disturbs the flow I had mentioned earlier.

For a long time, one could do a better job by importing external libraries, like catch-exception. To the rescue comes AssertJ, one of my favorite Java libraries (I even pushed some commits a couple of years ago!). The solution was possible thanks to the lambda expressions that came in Java 8, and it looks like this:

@Test
public void test_and_validate_exception() throws Exception {
    // given
    MyService testObject = new MyService();

    // expect
    Throwable thrown = catchThrowable(() -> { testObject.throwAnException() });

    // when
    assertThat(thrown).isInstanceOf(NullPointerException.class)
                      .hasNoCause()
                      .withStackTraceContaining("NullPointerException");              
}


Pure perfection! I feel sorry for myself for using JUnit extensions for so long, while the perfect solution was just under my nose! As you can see, there is a proper execution with the result, and a set of verifications after that. There is no inversion of execution, so you can do other validations — for example, verifying that a mock was (or wasn't) called.

Before AssertJ exception handling, it was hard to convince someone not to use the plain try-catch solution. But now I don't know why anyone would need to be convinced. The AssertJ approach is far superior, and I'm looking forward to starting using that in all my projects.

Strategies and techniques for building scalable and resilient microservices to refactor a monolithic application step-by-step, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
java ,junit ,assertj ,exception handling

Published at DZone with permission of Mateusz Haligowski. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}