Blake Caldwell: /dev/blog
Not a Java Developer

Asserting Exception Messages With JUnit Rules

If you’re not familiar with JUnit’s @Rule feature for asserting exceptions in your tests, then read on - you’re about to start using it.

Assert Exception Type

It’s very simple to assert that a given type of exception is thrown in a JUnit test case with the following:

public void testExceptionTypeIsThrown()
{
    throw new RuntimeException("Error!");
}

Assert Exception Message (The Old Way)

But, what if you want to be more specific, and check the message itself? I’ve always done the following:

/**
 * Test the exception message the old way.
 */
@Test
public void testExceptionMessageTheOldWay1()
{
    String message = null;
    try
    {
        throw new RuntimeException("Error!");
    }
    catch(Exception e)
    {
        message = e.getMessage();
    }
    Assert.assertEquals("Error!", message);
}

Heres’s another variant you’re probably familiar with:

/**
 * Test the exception message the old way.
 */
@Test
public void testExceptionMessageTheOldWay2()
{
    try
    {
        // this string test was necessary to avoid an "unreachable code" warning in Eclipse
        if ("A".equals("A"))
        {
            throw new RuntimeException("Error!");
        }
        Assert.fail("Exception was expected.");
    } catch (Exception e)
    {
        Assert.assertEquals("Error!", e.getMessage());
    }
}

Assert Exception Message With JUnit Rules

The above methods always felt like hacks. I recently came across JUnit’s @Rule feature, which saves tons of code and is much easier to read. You first define your public ExpectedException instance, and give it a @Rule annotation. Then, in each test case that wants to use it, you set what type of exception you’re expecting, and optionally a substring to look for in the exception message:

/**
 * Our expected exception rule. This must be public.
 */
@Rule
public ExpectedException exception = ExpectedException.none();

/**
 * Test the exception message with a single expectMessage substring match.
 */
@Test
public void testExceptionIsThrownFullText()
{
    exception.expect(RuntimeException.class);
    exception.expectMessage("Error!");

    throw new RuntimeException("Error!");
}

Since expectMessage is looking for substrings, you can use several of them to test more complicated exception messages:

/**
 * Our expected exception rule. This must be public.
 */
@Rule
public ExpectedException exception = ExpectedException.none();

/**
 * Test the exception message with multiple expectMessage substrings.
 */
@Test
public void testExceptionIsThrownMultipleSubstrings()
{
    exception.expect(RuntimeException.class);
    exception.expectMessage("Error: There was an issue processing ");
    exception.expectMessage(" of the notes.");

    throw new RuntimeException("Error: There was an issue processing 5 of the notes.");
}

More Advanced: Custom Matchers

In my next post, I’ll i describe how to implement a custom Matcher for more complicated Exception assertions.