Expected exceptions

Filed under: Java, Programming, TDD, — Tags: @Rule, Expected exception, JUnit, checked exception — Thomas Sundberg — 2015-11-20

Sometimes you want to verify that an exception is thrown in your code.

Let me show you three different ways to verify that the expected exception has been thrown.

The bullet proof approach

One solution that always works would be the one below:

@Test
public void should_throw_runtime_exception_naive() {
    try {
        throwExampleException();
        fail("Expected a RuntimeException");
    } catch (RuntimeException e) {
        assertThat(e.getMessage(), is("Oops!"));
    }
}

You execute the action the test should check and catches a specific exception. And then check that the expected message actually is the message you see.

What is the problem then? The only issue I can think of is that it is unnecessary verbose. I am not comfortable with test code that isn't straight through. In this case, there is a catch clause. In other cases there may be loops or even worse, conditions.

So to me, this works but it isn't pretty.

Annotate the test

An approach that I often use is to annotate the test with the expected exception. It can look like this:

@Test(expected = RuntimeException.class)
public void should_throw_runtime_exception() {
    throwExampleException();
}

This is smaller. And smaller is good. But it is also a bit blunt. You will get feedback from the test when the expected exception isn't thrown. But you will not get feedback if the message is something different from what you expected.

Given these two options, I usually prefer the last one. Even when I am not able to verify the message. The compactness of the code is appealing.

If I were able to use an annotation like @Test(expected = RuntimeException.class, messgae = "Oops!") then that would have been a very nice solution. But JUnit doesn't support that.

There is a third way to do this. That is to use a JUnit @Rule annotation with ExpectedException. Let me show you how.

JUnit rule

You need to define a JUnit rule. And use it. It can be done like this:

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

@Test
public void should_also_throw_runtime_exception() {
    thrown.expect(RuntimeException.class);
    thrown.expectMessage("Oops!");

    throwExampleException();
}

This rule says that I don't expect any exceptions to be thrown from my method. This means that the behaviour is consistent with the default behaviour of JUnit.

I can, however, define that an exception should be thrown in a test and define the class for the exception.This is what I do in should_also_throw_runtime_exception(). And all of a sudden, I am not only able to assert the class for the thrown exception, I am also able to assert the message that was thrown. And I am able to do it in a test method where I don't have any blocks.

I think it would have been nice to extend the test annotation to handle both exception and its message, but using this rule construction is almost just as good as an extended annotation. I am able to verify more things, using the field thrown in this example, so I guess it is a resonable limitation.

Complete source code

I don't like blog posts that hides stuff from me. Especially imports in example code. It makes the examples magic and I am not impressed by magic. The complete source code I used for this blog is therefore included below.

Enjoy!

package se.thinkcode;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.fail;

public class CheckExceptionsTest {

    @Test
    public void should_throw_runtime_exception_naive() {
        try {
            throwExampleException();
            fail("Expected a RuntimeException");
        } catch (RuntimeException e) {
            assertThat(e.getMessage(), is("Oops!"));
        }
    }

    @Test(expected = RuntimeException.class)
    public void should_throw_runtime_exception() {
        throwExampleException();
    }

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

    @Test
    public void should_also_throw_runtime_exception() {
        thrown.expect(RuntimeException.class);
        thrown.expectMessage("Oops!");

        throwExampleException();
    }

    private void throwExampleException() {
        throw new RuntimeException("Oops!");
    }
}

Acknowledgements

I would like to thank Malin Ekholm for proof reading.

References



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
Executable specification
Gradle
Guice
J2EE
JUnit
Java
Linux
Load testing
Maven
Mockito
Pair programming
PicoContainer
Programming
Public speaking
Quality
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS