Creating spies using annotations

As usual, Mockito offers you the chance to remove a number of lines of code to make your tests more readable and clear. In this recipe, we will remove unnecessary code and convert it into annotations. As a reminder, you should have very legitimate reasons to use a spy in your code. Otherwise, it most likely signifies that there is something wrong with the design of your code.

Getting ready

For this recipe, we will reuse the example from the previous recipe. However, let's take another look at it. Our system under test for this recipe will be a TaxFactorProcessor class that interacts with a TaxService class in order to calculate the tax factor and update the tax data of a given person. Have a look at the following code:

public class TaxFactorProcessor {

    public static final double INVALID_TAX_FACTOR = -1;

    private final TaxService taxService;

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

    public double processTaxFactorFor(Person person) {
        try {
            double taxFactor = taxService.calculateTaxFactorFor(person);
            taxService.updateTaxData(taxFactor, person);
            return taxFactor;
        } catch (Exception e) {
            System.err.printf("Exception [%s] occurred while trying to calculate tax factor for person [%s]%n", e, person.getName());
            return INVALID_TAX_FACTOR;
        }
    }

}

We will test our system to check that TaxService performs computations but does not call a web service in our unit test.

How to do it...

To profit from Mockito's annotations, you need to perform the following steps:

  1. For JUnit, annotate your test class with @RunWith(MockitoJUnitRunner.class). For TestNG, copy the necessary TestNG listeners and annotate your test class with @Listeners(MockitoTestNGListener.class). Refer to Chapter 1, Getting Started with Mockito, for more details on the TestNG setup.
  2. Annotate the object you want to create a spy for with the @Spy annotation.

For both scenarios, remember that I'm using the AssertJ's BDDAssertions.then(...) static method. Refer to Chapter 7, Verifying Behavior with Object Matchers, to know how to work with AssertJ or how to do the same with Hamcrest's assertThat(...).

Now, let's take a look at the test written for JUnit. Have a look at the following code:

@RunWith(MockitoJUnitRunner.class)
public class TaxFactorProcessorTest {

    @Spy TaxService taxService;

    @InjectMocks TaxFactorProcessor systemUnderTest;

    @Test
    public void should_return_default_tax_factor_for_person_from_undefined_country() {
        // given
        doNothing().when(taxService).updateTaxData(anyDouble(), any(Person.class));

        // when
        double taxFactor = systemUnderTest.processTaxFactorFor(new Person());

        // then
        then(taxFactor).isEqualTo(TaxService.DEFAULT_TAX_FACTOR);
    }

}

For TestNG, the test is written as follows:

@Listeners(MockitoTestNGListener.class)
public class TaxFactorProcessorTestNgTest {

    @Spy TaxService taxService;

    @InjectMocks TaxFactorProcessor systemUnderTest;

    @Test
    public void should_return_default_tax_factor_for_person_from_undefined_country() {
        // given
        doNothing().when(taxService).updateTaxData(anyDouble(), any(Person.class));

        // when
        double taxFactor = systemUnderTest.processTaxFactorFor(new Person());

        // then
        then(taxFactor).isEqualTo(TaxService.DEFAULT_TAX_FACTOR);
    }

}

How it works...

The creation of spies based on annotations works exactly the same as the mocks presented in the Creating Mocks with annotations recipe of Chapter 2, Creating Mocks. Please refer to that chapter for more details.

There's more...

Let's take another look at the @Spy annotated field from our test:

@Spy TaxService taxService

What if you want to create a spy of an object that you want to instantiate in a special way? What if, in our example, TaxService doesn't have a default constructor and we need to provide some explicit value to initialize it?

Before we answer that question, let's check how Mockito works for spy initialization. If you annotate a field with @Spy, Mockito will initialize it if its zero argument constructor can be found. The scope of the constructor doesn't need to be public; it can be private too. What Mockito can't do is instantiate local or inner interfaces and classes.

Coming back to the question, how can we create a spy and provide its initialization parameters? You need to explicitly call the object's constructor as follows:

@Spy TaxService taxService = new TaxService("Some value");

See also

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.52.188