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:
1 2 3 4 5 6 | < 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 | [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):
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 | 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class CustomException extends RuntimeException { private final int code; public CustomException( int code) { this .code = code; } public int getCode() { return code; } } class ExceptionCodeMatches extends TypeSafeMatcher<CustomException> { 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:
01 02 03 04 05 06 07 08 09 10 11 12 13 | 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:
- 3 ways of handling exceptions in JUnit. Which one to choose?
- JUnit ExpectedException rule: beyond basics
- HOW-TO: Test dependencies in a Maven project (JUnit, Mocito, Hamcrest, AssertJ)
- Improve content assist for types with static members while creating JUnit tests in Eclipse
Reference: | Yet another way to handle exceptions in JUnit: catch-exception from our JCG partner Rafal Borowiec at the Codeleak.pl blog. |