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.