Mockito Cookbook
上QQ阅读APP看书,第一时间看更新

Adding Mockito hints to exception messages (JUnit) (Experimental)

When a JUnit test fails, an exception is thrown and a message is presented. Sometimes, it is enough to find a reason for this mistake and to find the solution. Mockito, however, goes a step further and tries to help the developer by giving him additional hints regarding the state of the stubbed methods.

Note

Remember that this feature is experimental and the API, name, or anything related to it may change in time. What is more, the whole feature may get deleted in time!

Getting ready

For this recipe, let's assume that our system is the MeanTaxFactorCalculator class that calculates tax through TaxService, which has two methods—performAdditionalCalculation() and getCurrentTaxFactorFor(...). For the sake of this example, let's assume that only the latter is used to calculate the mean value:

public class MeanTaxFactorCalculator {

    private final TaxService taxService;

    public MeanTaxFactorCalculator(TaxService taxService) {
        this.taxService = taxService;
    }

    public double calculateMeanTaxFactorFor(Person person) {
        double currentTaxFactor = taxService.getCurrentTaxFactorFor(person);
        double anotherTaxFactor = taxService.getCurrentTaxFactorFor(person);
        return (currentTaxFactor + anotherTaxFactor) / 2;
    }

}

We wanted to check whether our system under test is calculating the proper result, so we wrote the following test but made a mistake and stubbed a wrong method (I'm using the BDDMockito.given(...) and AssertJ's BDDAssertions.then(...) static methods—refer Chapter 7, Verifying Behavior with Object Matchers, for how to work with AssertJ or how to do the same with Hamcrest's assertThat(...) method):

@RunWith(MockitoJUnitRunner.class)
public class MeanTaxFactorCalculatorTest {

    static final double UNUSED_VALUE = 10;

    @Test
    public void should_calculate_mean_tax_factor() {
        // given
        TaxService taxService = given(Mockito.mock(TaxService.class).performAdditionalCalculation()).willReturn(UNUSED_VALUE).getMock();
        MeanTaxFactorCalculator systemUnderTest =new MeanTaxFactorCalculator(taxService);

        // when
        double meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new Person());

        // then
        then(meanTaxFactor).isEqualTo(UNUSED_VALUE);
    }

}

The test fails and what we can see is the standard JUnit comparison failure being thrown (presenting only the most important part of the stack trace) as follows:

org.junit.ComparisonFailure:
Expected :10.0
Actual   :0.0

Now let's take a look at how to use Mockito's experimental features to get more Mockito related information appended to the error message.

How to do it...

If you want to have more information presented in your error message, you have to perform the following steps:

  1. Annotate your JUnit test with @RunWith(VerboseMockitoJUnitRunner.class).
  2. Define your mocks and perform stubbing inside the test method (unfortunately, you can't use annotations or initialize fields outside test methods).

What happens next is that additional exception messages can be seen in the exception that is thrown as follows:

org.mockito.internal.exceptions.ExceptionIncludingMockitoWarnings:contains both: actual test failure *and* Mockito warnings.
This stubbing was never used   -> at ...MeanTaxFactorCalculatorTest.should_calculate_mean_tax_factor(MeanTaxFactorCalculatorTest.java:30)

 *** The actual failure is because of: ***

Expected :10.0
Actual   :0.0

How it works...

When the test is run, VerboseMockitoJUnitRunner appends a listener. When the test is started, this listener finds all the stubs through WarningsCollector, including the unused stubs for given mocks.

As the Mockito developers state it in the code, they are indeed using a very hacky way to append a message to the thrown exception after the test fails. The JUnitFailureHacker class is instantiated and, by means of the Whitebox class, the internal state of a private field of the JUnit's Failure object is modified with additional Mockito messages.