Traditional exception handling in JUnit tests
You’re probably used to testing code that throws exceptions in either one of the following two ways:
JUnit
's@Test
annotationexpected
parameter
@Test(expected = UserService.NoSuchUserException.class)
public void deletesExistingUser() {
final User existingUser = spy(new User());
given(userRepository.findOne(eq("123"))).willReturn(existingUser);
given(userRepository.save(eq(existingUser))).willReturn(existingUser);
userService.deleteUser("123");
verify(existingUser).setState(eq(User.State.DELETED));
}
The disadvantage of this approach is that we can't be sure that the exception thrown was thrown by the code we expect to throw it
- JUnit’s
ExpectedException @Rule
:
To gain control over which part of test should throw an exception, we can use ExpectedException
JUnit
rule, here's an example:
@Rule
ExpectedException thrown = ExpectedException.none();
@Test
public void deletesExistingUser() {
thrown.expect(UserService.NoSuchUserException.class);
thrown.expectMessage(format("User with id %s doesn't exist!", userId));
final User existingUser = spy(new User());
given(userRepository.findOne(eq("123"))).willReturn(existingUser);
given(userRepository.save(eq(existingUser))).willReturn(existingUser);
userService.deleteUser("123");
verify(existingUser).setState(eq(User.State.DELETED));
}
Whereas the second approach gives more control over expressing where the test code should throw
an exception, there's even better approach to design your JUnit
tests, using powerful
AssertJ testing library.
AssertJ approach
The one I found quite elegant and easy to use and understand comes from powerful AssertJ
library, using static Assertions.assertThatThrownBy
static method, as shown in the following
test:
@Test
public void failsDeletingUser() {
final String userId = "123";
given(userRepository.findOne(eq(userId))).willReturn(null);
assertThatThrownBy(() -> userService.deleteUser(userId))
.isInstanceOf(UserService.NoSuchUserException.class)
.hasMessage(format("User with id %s doesn't exist!", userId));
}
It receives instance of ThrowingCallable
functional interface (Java 8) which is quite handy
since you can pass lambda implementation (as shown). Given interface has only one method,
which we implemented as lambda above. The interface looks like:
public interface ThrowingCallable {
void call() throws Throwable;
}
In upcoming version 5 of JUnit
framework there’s no more option to use expected attribute in
@Test
annotation. Having two options there, my favourite will be option coming from AssertJ
library.
Let me know your thoughts!
That was all for today! Hope you liked it!