package org.mockitoutil; import org.assertj.core.api.AbstractThrowableAssert; import org.assertj.core.api.Assertions; import org.junit.rules.MethodRule; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.Statement; /** * Junit rule for testing exception handling other JUnit rules, like Mockito JUnit rules. * Makes it easy to assert on expected exceptions triggered by the rule under test. */ public class SafeJUnitRule implements MethodRule { private final MethodRule testedRule; private FailureAssert failureAssert = null; private Runnable successAssert = new Runnable() { public void run() { } }; /** * Wraps rule under test with exception handling so that it is easy to assert on exceptions fired from the tested rule. */ public SafeJUnitRule(MethodRule testedRule) { this.testedRule = testedRule; } public Statement apply(final Statement base, final FrameworkMethod method, final Object target) { return new Statement() { public void evaluate() throws Throwable { try { testedRule.apply(base, method, target).evaluate(); successAssert.run(); } catch (Throwable t) { if (failureAssert == null) { throw t; } failureAssert.doAssert(t); return; } if (failureAssert != null) { //looks like the user expects a throwable but it was not thrown! throw new ExpectedThrowableNotReported("Expected the tested rule to throw an exception but it did not."); } } }; } /** * Expects that _after_ the test, the rule will fire specific exception with specific exception message */ public void expectFailure(final Class<? extends Throwable> expected, final String expectedMessage) { this.expectFailure(new FailureAssert() { public void doAssert(Throwable t) { assertThrowable(t, expected).hasMessage(expectedMessage); } }); } /** * Expects that _after_ the test, the rule will fire specific exception with specific exception message */ public void expectFailure(final Class<? extends Throwable> expected) { this.expectFailure(new FailureAssert() { public void doAssert(Throwable t) { assertThrowable(t, expected); } }); } private static AbstractThrowableAssert assertThrowable(Throwable throwable, Class<? extends Throwable> expected) { return Assertions.assertThat(throwable).isInstanceOf(expected); } /** * Expects that _after_ the test, the rule will fire an exception */ public void expectFailure(FailureAssert failureAssert) { this.failureAssert = failureAssert; } /** * Expects that _after_ the test, given assert will run */ public void expectSuccess(Runnable successAssert) { this.successAssert = successAssert; } /** * Offers a way to assert the throwable triggered by the tested rule */ public interface FailureAssert { void doAssert(Throwable t); } /** * Thrown when user expects the tested rule to throw an exception but no exception was thrown */ class ExpectedThrowableNotReported extends Throwable { public ExpectedThrowableNotReported(String message) { super(message); } } }