Testing Exceptions in JUnit 4.7
JUnit 4.7 introduced a few features that make it a little easier to work with exceptions. JUnit 4 introduced the expected parameter, which makes a test succeed if and only if a certain exception is thrown. For example, in the following code sample, we are testing a UserManager class. When the login() function is called, it should throw an UnknownUserException when no user is found. Our UserDao class will return null in this case, so we mock out the UserDao class (using Mockito here), and use the expected parameter to ensure that the exception is thrown:
UserManager manager;
@Before
public void setup() {
manager = new UserManagerImpl();
}
@Test(expected=UnknownUserException.class)
public void shouldNotLetUnknownUserLogin() throws UnknownUserException {
UserDao dao = mock(UserDao.class);
when(dao.findByName("joe")).thenReturn(null);
manager.setUserDao(dao);
manager.login("joe", "secret");
}
This approach is useful for simple cases, but it has its limits. For example, you can't test the value of the message in the exception, or the state of a domain object after the exception has been thrown. In JUnit 4.7, however, you can now use the ExpectedException rule to address the first issue, if not the second. This rule lets you indicate not only what exception you are expecting, but also the exception message you are expecting, in a very readable mock-object style:
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void shouldNotLetUnknownUserLogin() throws UnknownUserException {
thrown.expect(UnknownUserException.class);
thrown.expectMessage("Unknown user: joe");
UserDao dao = mock(UserDao.class);
when(dao.findByName("joe")).thenReturn(null);
manager.setUserDao(dao);
manager.login("joe","secret");
}
The expectMessage also lets you use Matchers, which gives you a bit more flexibility in your tests. For example, I could also have written something like the following:
thrown.expectMessage(JUnitMatchers.containsString("joe"));
This is great if all you need to do is test the exception thrown and the associated message. However if you need to check the state of the domain objects that are manipulated during the tests, you start to run into the limits of JUnit 4.7's new features. One option would be to use the Verifier rule, which lets you check the state of your objects after each test and force the test to fail under certain circumstances, as shown here:
@Rule
public MethodRule verifier = new Verifier() {
@Override public void verify() {
assertThat(manager.getSessionStatus("joe"), is(LOGGED_OUT));
}
}
}
However, since this method will be called after every test, it would not scale well - indeed, this is probably not really the intended use of Verifier rules, which should ideally be state checks that would be applicable for all of the tests in your class. So, before you get too wrapped up in complicated solutions, you might simply want to fall back on the old try-catch-fail strategy in these cases.
John is a freelance consultant specialising in Enterprise Java, Web Development, and Open Source technologies, currently based in Wellington, New Zealand. Well known in the Java community for his many published articles, John helps organisations to optimize their Java development processes and infrastructures and provides training and mentoring in open source technologies, SDLC tools, and agile development processes. John is principal consultant at Wakaleo Consulting, and runs several Training Courses on open source Java development tools and best practices. John is a DZone MVB and is not an employee of DZone and has posted 57 posts at DZone. You can read more from them at their website.
- mr_john_smart's blog
- Login or register to post comments
- 1921 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)









