Unit testing exceptions in Java

Testing exceptions is crucial when you are into defensive programming. Ensuring your code behaves correctly under unforeseen circumstances. Though JUnit’s @Test(expected = ...) seems to be good enough, there are cases where you might find your tests green when they shouldn’t. The bad thing is it verifies whether an exception was thrown in whole test method:

@Test(expected = ExpectedException.class)
public void testException() {
	// given
	preparingToTest();

	// when
	testIfThisMethodThrowsException();

	// then an exception is thrown
}

At first this test looks ok but what if preparingToTest() would throw ExpectedException? The test would still be green even though the testIfThisMethodThrowsException() was never executed. Because of that some people try to stick with JUnit3-style exception catching. Take a look at the same test written using JUnit3:

@Test 
public void testException() {
	// given
	preparingToTest();
	
	// when
	ExpectedException caughtException = null;
	try {
		testIfThisMethodThrowsException();
		fail("Expected exception to be thrown");
	} catch (ExpectedException e) {
		caughtException = e;
	}
	
	// then
	assertThat(caughtException).isNotNull();
}

There are several problems with this test. We have a rule of thumb - code inside ‘when’ statement should not contain more than one operation. But as you can see it does a lot. You need to initialize caughtException with null (which is easy to forget), perform the exceptional operation, catch and assign thrown exception. Because of that it’s not very clear what we are doing here.

That’s why we came up with ExceptionCatcher (part of the sweetener project. It does the same logic, but in a lot cleaner way. Take a look at the test:

@Test
public void shouldThrowAnExpectedException() {
	// when
	ExpectedException caughtException = ExceptionCatcher.tryToCatch(ExpectedException.class, new ExceptionalOperation() {
		@Override
		public void operate() throws Exception {
			operationThatWillThrowException();
		}
	});
	// then
	assertThat(caughtException).isNotNull();
}

If you are lucky to work with java 8 it goes even better with lambda expression:

@Test
public void shouldThrowAnExpectedException() {
	// when
	ExpectedException caughtException = tryToCatch(ExpectedException.class, () -> {
		operationThatWillThrowException();
	});
	// then
	assertThat(caughtException).isNotNull();
}

And better with method reference:

@Test
public void shouldThrowAnExpectedException() {
	// when
	ExpectedException caughtException = tryToCatch(ExpectedException.class, this::operationThatWillThrowException);

	// then
	assertThat(caughtException).isNotNull();
}

As you can see we’re trying to catch ExceptedException which operationThatWillThrowException() throws and we’re verifying that caughtException is not null.

Simple and clean.


Łukasz Stypka

Java experienced enthusiast, opened for new technologies. Clean code and simple solution fan.

Tomasz Kuryłek

Self-styled and passionate java hipster. Focused on solution that makes user and himself ecstatic..