Skip to main content

Testing Exceptions in JUnit 4.7

Posted by johnsmart on September 27, 2009 at 1:56 PM PDT

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.

"Probably the best training course I've been on."..."Not just how to write Java code but the 'business end' - how to build, test, deploy, manage and monitor"..."One of the best and most useful courses I have attended. And they didn't even try to sell me anything!" Hurry! Sessions coming up very soon in Sydney and Wellington  - Get up to speed with the latest and coolest in Java tools and best practices! Sign up on the 2009 season of the Java Power Tools Bootcamps.

Related Topics >>

Comments

Thanks

Thank you for providing this nice simple primer on how to use the new exception handling along with rules.