Yet another way to handle exceptions in JUnit: catch-exception
There are many ways of handling exceptions in JUnit (3 ways of handling exceptions in JUnit. Which one to choose?, JUnit ExpectedException rule: beyond basics). In this post I will introduce catch-exception library that I was recommended to give a try. In short, catch-exceptions is a library that catches exceptions in a single line of code and makes them available for further analysis.
Install via Maven
In order to get started quickly, I used my Unit Testing Demo project with a set of test dependencies (JUnit, Mocito, Hamcrest, AssertJ) and added catch-exceptions:
<dependency>
<groupId>com.googlecode.catch-exception</groupId>
<artifactId>catch-exception</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
So the dependency tree looks as follows:
[INFO] --- maven-dependency-plugin:2.1:tree @ unit-testing-demo --- [INFO] com.github.kolorobot:unit-testing-demo:jar:1.0.0-SNAPSHOT [INFO] +- org.slf4j:slf4j-api:jar:1.5.10:compile [INFO] +- org.slf4j:jcl-over-slf4j:jar:1.5.10:runtime [INFO] +- org.slf4j:slf4j-log4j12:jar:1.5.10:runtime [INFO] +- log4j:log4j:jar:1.2.15:runtime [INFO] +- junit:junit:jar:4.11:test [INFO] +- org.mockito:mockito-core:jar:1.9.5:test [INFO] +- org.assertj:assertj-core:jar:1.5.0:test [INFO] +- org.hamcrest:hamcrest-core:jar:1.3:test [INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test [INFO] +- org.objenesis:objenesis:jar:1.3:test [INFO] \- com.googlecode.catch-exception:catch-exception:jar:1.2.0:test
Getting started
System under test (SUT):
class ExceptionThrower {
void someMethod() {
throw new RuntimeException("Runtime exception occurred");
}
void someOtherMethod() {
throw new RuntimeException("Runtime exception occurred",
new IllegalStateException("Illegal state"));
}
void yetAnotherMethod(int code) {
throw new CustomException(code);
}
}
The basic catch-exception BDD-style approach example with AssertJ assertions:
import org.junit.Test;
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;
public class CatchExceptionsTest {
@Test
public void verifiesTypeAndMessage() {
when(new SomeClass()).someMethod();
then(caughtException())
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occured")
.hasMessageContaining("exception")
.hasNoCause();
}
}
Looks good. Concise, readable. No JUnit runners. Please note, that I specified which method of SomeClass I expect to throw an exception. As you can imagine, I can check multiple exceptions in one test. Although I would not recommend this approach as it may feel like violating a single responsibility of a test.
By the way, if you are working with Eclipse this may be handy for you: Improve content assist for types with static members while creating JUnit tests in Eclipse
Verify the cause
I think there is no comment needed for the below code:
import org.junit.Test;
import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionAssertJ.*;
public class CatchExceptionsTest {
@Test
public void verifiesCauseType() {
when(new ExceptionThrower()).someOtherMethod();
then(caughtException())
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasCauseExactlyInstanceOf(IllegalStateException.class)
.hasRootCauseExactlyInstanceOf(IllegalStateException.class);
}
}
Verify custom exception with Hamcrest
To verify a custom exception I used the Hamcrest matcher code from my previous post:
class CustomException extends RuntimeException {
private final int code;
public CustomException(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
class ExceptionCodeMatches extends TypeSafeMatcher {
private int expectedCode;
public ExceptionCodeMatches(int expectedCode) {
this.expectedCode = expectedCode;
}
@Override
protected boolean matchesSafely(CustomException item) {
return item.getCode() == expectedCode;
}
@Override
public void describeTo(Description description) {
description.appendText("expects code ")
.appendValue(expectedCode);
}
@Override
protected void describeMismatchSafely(CustomException item, Description mismatchDescription) {
mismatchDescription.appendText("was ")
.appendValue(item.getCode());
}
}
And the test:
import org.junit.Test;
import static com.googlecode.catchexception.CatchException.*;
import static org.junit.Assert.*;
public class CatchExceptionsTest {
@Test
public void verifiesCustomException() {
catchException(new ExceptionThrower(), CustomException.class).yetAnotherMethod(500);
assertThat((CustomException) caughtException(), new ExceptionCodeMatcher(500));
}
}
Summary
catch-exception looks really good. It is easy to get started quickly. I see some advantages over method rule in JUnit. If I have a chance, I will investigate the library more thoroughly, hopefully in a real-world project.
The source code of this article can be found here: Unit Testing Demo
In case you are interested, please have a look at my other posts: