JUnit: testing exception with Java 8 and Lambda Expressions
In JUnit there are many ways of testing exceptions in test code, including try-catch idiom, JUnit @Rule, with catch-exception library. As of Java 8 we have another way of dealing with exceptions: with lambda expressions. In this short blog post I will demonstrate a simple example how one can utilize the power of Java 8 and lambda expressions to test exceptions in JUnit.
Note: The motivation for writing this blog post was the message published on the catch-exception project page:
Java 8’s lambda expressions will make catch-exception redundant. Therefore, this project won’t be maintained any longer
SUT – System Under Test
We will test exceptions thrown by the below 2 classes.
The first one:
class DummyService {
public void someMethod() {
throw new RuntimeException("Runtime exception occurred");
}
public void someOtherMethod() {
throw new RuntimeException("Runtime exception occurred",
new IllegalStateException("Illegal state"));
}
}And the second:
class DummyService2 {
public DummyService2() throws Exception {
throw new Exception("Constructor exception occurred");
}
public DummyService2(boolean dummyParam) throws Exception {
throw new Exception("Constructor exception occurred");
}
}Desired Syntax
My goal was to achieve syntax close to the one I had with catch-exception library:
package com.github.kolorobot.exceptions.java8;
import org.junit.Test;
import static com.github.kolorobot.exceptions.java8.ThrowableAssertion.assertThrown;
public class Java8ExceptionsTest {
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod) // method reference
// assertions
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasNoCause();
}
@Test
public void verifiesCauseType() {
assertThrown(() -> new DummyService().someOtherMethod(true)) // lambda expression
// assertions
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasCauseInstanceOf(IllegalStateException.class);
}
@Test
public void verifiesCheckedExceptionThrownByDefaultConstructor() {
assertThrown(DummyService2::new) // constructor reference
// assertions
.isInstanceOf(Exception.class)
.hasMessage("Constructor exception occurred");
}
@Test
public void verifiesCheckedExceptionThrownConstructor() {
assertThrown(() -> new DummyService2(true)) // lambda expression
// assertions
.isInstanceOf(Exception.class)
.hasMessage("Constructor exception occurred");
}
@Test(expected = ExceptionNotThrownAssertionError.class) // making test pass
public void failsWhenNoExceptionIsThrown() {
// expected exception not thrown
assertThrown(() -> System.out.println());
}
}Note: The advantage over catch-exception is that we will be able to test constructors that throw exceptions.
Creating the ‘library’
Syntatic sugar
assertThrown is a static factory method creating a new instance of ThrowableAssertion with a reference to caught exception.
package com.github.kolorobot.exceptions.java8;
public class ThrowableAssertion {
public static ThrowableAssertion assertThrown(ExceptionThrower exceptionThrower) {
try {
exceptionThrower.throwException();
} catch (Throwable caught) {
return new ThrowableAssertion(caught);
}
throw new ExceptionNotThrownAssertionError();
}
// other methods omitted for now
}The ExceptionThrower is a @FunctionalInterface which instances can be created with lambda expressions, method references, or constructor references. assertThrown accepting ExceptionThrower will expect and be ready to handle an exception.
@FunctionalInterface
public interface ExceptionThrower {
void throwException() throws Throwable;
}Assertions
To finish up, we need to create some assertions so we can verify our expactions in test code regarding teste exceptions. In fact, ThrowableAssertion is a kind of custom assertion providing us a way to fluently verify the caught exception. In the below code I used Hamcrest matchers to create assertions. The full source of ThrowableAssertion class:
package com.github.kolorobot.exceptions.java8;
import org.hamcrest.Matchers;
import org.junit.Assert;
public class ThrowableAssertion {
public static ThrowableAssertion assertThrown(ExceptionThrower exceptionThrower) {
try {
exceptionThrower.throwException();
} catch (Throwable caught) {
return new ThrowableAssertion(caught);
}
throw new ExceptionNotThrownAssertionError();
}
private final Throwable caught;
public ThrowableAssertion(Throwable caught) {
this.caught = caught;
}
public ThrowableAssertion isInstanceOf(Class<? extends Throwable> exceptionClass) {
Assert.assertThat(caught, Matchers.isA((Class<Throwable>) exceptionClass));
return this;
}
public ThrowableAssertion hasMessage(String expectedMessage) {
Assert.assertThat(caught.getMessage(), Matchers.equalTo(expectedMessage));
return this;
}
public ThrowableAssertion hasNoCause() {
Assert.assertThat(caught.getCause(), Matchers.nullValue());
return this;
}
public ThrowableAssertion hasCauseInstanceOf(Class<? extends Throwable> exceptionClass) {
Assert.assertThat(caught.getCause(), Matchers.notNullValue());
Assert.assertThat(caught.getCause(), Matchers.isA((Class<Throwable>) exceptionClass));
return this;
}
}AssertJ Implementation
In case you use AssertJ library, you can easily create AssertJ version of ThrowableAssertion utilizing org.assertj.core.api.ThrowableAssert that provides many useful assertions out-of-the-box. The implementation of that class is even simpler than with Hamcrestpresented above.
package com.github.kolorobot.exceptions.java8;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.ThrowableAssert;
public class AssertJThrowableAssert {
public static ThrowableAssert assertThrown(ExceptionThrower exceptionThrower) {
try {
exceptionThrower.throwException();
} catch (Throwable throwable) {
return Assertions.assertThat(throwable);
}
throw new ExceptionNotThrownAssertionError();
}
}An example test with AssertJ:
public class AssertJJava8ExceptionsTest {
@Test
public void verifiesTypeAndMessage() {
assertThrown(new DummyService()::someMethod)
.isInstanceOf(RuntimeException.class)
.hasMessage("Runtime exception occurred")
.hasMessageStartingWith("Runtime")
.hasMessageEndingWith("occurred")
.hasMessageContaining("exception")
.hasNoCause();
}
}Summary
With just couple of lines of code, we built quite cool code helping us in testing exceptions in JUnit without any additional library. And this was just a start. Harness the power of Java 8 and lambda expressions!
Resources
- Source code for this article is available on GitHub (have a look at
com.github.kolorobot.exceptions.java8package) - Some other articles of mine about testing exceptions in JUnit. Please have a look:
| Reference: | JUnit: testing exception with Java 8 and Lambda Expressions from our JCG partner Rafal Borowiec at the Codeleak.pl blog. |

