Core Java

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

  1. The expected parameter of the @Test annotation and
  2. 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.

Peter Daum

Peter is senior Java developer in the telecommunication industry. He works in the area of business and operations support systems and is always happy to share his knowledge and experience. He is interested in everything related to Java and software craftsmanship.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Drouard
Drouard
7 years ago

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.

Peter
Peter
7 years ago
Reply to  Drouard

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 »

Back to top button