JUnit rules – executing additional verification when exceptions are thrown
In this post I will show you quickly how handy JUnit rules are if you need to solve the following challenge
A method catches an exception and has to perform some extra tasks before rethrowing or throwing a wrapper exception.
Calls to the extra tasks and the thrown exception should be verified by a unit test.
This means you have some code like this
public class MyThrowingClass { private final ExceptionProcessor exceptionProcessor; public MyThrowingClass(ExceptionProcessor exceptionProcessor) { this.exceptionProcessor = exceptionProcessor; } public void runTask() throws NullPointerException { try { // something to do here throw new NullPointerException("It's null Jim"); } catch (NullPointerException e) { exceptionProcessor.process(e); // This call needs to be verified throw e; } } }
And call in this line
exceptionProcessor.process(e);
needs to be verified as well as the thrown exception.
Straight forward … but ugly
I will not go into details of this variant
try { cut.runMyMethod(); } catch(Exception e) { verify(...); assertThat(e).isInstanceOf(); }
as I personally try to avoid try catch constructs in my test code if feasible.
The easy one first
Verifying that the exception is thrown is fairly easy, JUnit provides to potential options here
- The expected parameter of the @Test annotation and
- a rule called ExceptionRule
The first option will look like this
@Test(expected = NullPointerException.class) public void myTestWithExpectedParameter() throws Exception { // ... }
the second one like this
// ... @Rule public ExceptionRule exceptionRule = ExceptionRule.none(); // ... @Test public void myTestWithTheExceptionRule() throws Exception { exceptionRule.expect(NullPointerException.class); // ... }
No it’s getting a little bit more complicated
The problem behind the mentioned requirement for testing is the following
All the verify( … ) steps you do, after executing your method under test, will not be executed as the exception stops the rest of the test method execution as usual if exceptions are thrown and not caught.
JUnit rules for the rescue
With JUnit rules, we can easily create a way to provide additional verification steps even in case of exceptions thrown.
I’m aware that JUnit provides already a verifier rule, but I will not use it. This class has the drawback that the verification logic is burned into it, when it’s setup.
So what we need is a rule that allows us to specify per test an additional verification logic which is applied after the test is executed.
The general usage should look like this
@Rule public VerifyRule verifyRule = new VerifyRule(); @Mock ExceptionProcessor exceptionProcessor; @Test() public void working() throws Exception { verifyRule.setVerifier(() -> verify(exceptionProcessor).process(any())); // .. }
To get this up and running we need to things
- the VerifyRule
- any kind of callback interface that can be set on the verify rule
Let’s start with the callback interface
public interface VerifyRuleCallback { void execute() throws Throwable; }
Nothing special here, as you can see.
Now let’s focus on the VerifyRule
public class VerifyRule implements TestRule { private VerifyRuleCallback verifyRuleCallback; @Override public Statement apply(Statement base, Description description) { return new VerifyRuleStatement(base); } public void setVerifier(VerifyRuleCallback verifyRuleCallback) { this.verifyRuleCallback = verifyRuleCallback; } private class VerifyRuleStatement extends Statement { private final Statement nextStatement; public VerifyRuleStatement(Statement nextStatement) { this.nextStatement = nextStatement; } @Override public void evaluate() throws Throwable { nextStatement.evaluate(); verifyRuleCallback.execute(); } } }
As you can see it implements the TestRule interface and provides a method to set the VerifyRuleCallback. The callback is then used within the evaluate method of the VerifyRuleStatement that needs to be implemented to run our own callback evaluation.
Tying it all together
With the new rule and the callback a test could look like this
public class MyThrowingClassShould { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @InjectMocks MyThrowingClass cut; @Mock ExceptionProcessor processor; @Rule public ExpectedException exception = ExpectedException.none(); @Rule public VerifyRule verifyRule = new VerifyRule(); @Test() public void execute_the_exception_processor_and_rethrow_the_exception_when_it_occur() throws Exception { verifyRule.setVerifier(() -> verify(processor).process(any(NullPointerException.class))); exception.expect(NullPointerException.class); cut.runTask(); } }
Summary
As we have seen JUnit rules provide a very nice and easy way to create clean and understandable test code when, and not only in this case, these kind of requirements on testing came up.
Reference: | JUnit rules – executing additional verification when exceptions are thrown from our JCG partner Peter Daum at the Coders Kitchen blog. |
Your post only proved that it still is a pain to test that kind of behavior. It is NOT at all clean and understandable : you still have to put a lot of effort in boilerplate tuning code to make a simple verification after an exception is thrown.
Something like https://github.com/Codearte/catch-exception library would be a much more understable way to handle after thrown exception checks.
Hi Drouard, thanks for your comment and pointing me to that library. I wasn’t aware of this. It looks very nice. Even if I don’t get your reasoning about the effort and boilerplate code, I agree that the option provided by the library you mentioned, provides a clean alternative way to achieve this goal. The drawbacks I see with the library is that you must go for PowerMock in case of final classes, if I got the documentation right. I personally prefer not to introduce it (PowerMock) if feasible. In this case the solution I provided is indeed a clean… Read more »