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

Creating mocks with custom configuration

Even though in the majority of cases you will not need the feature discussed in the preceding recipe, sometimes you may want your mock to satisfy some additional prerequisites. Thanks to Mockito's Mockito.withSettings fluent interface, you can easily set up your custom MockitoSettings object that you can pass to the Mockito.mock method that will create your mock. When you check out the Javadoc of MockitoSettings, you will see a note that you shouldn't use that class too often. That's good advice, because you should make it a practice to write your code and tests in such a way that it is either of high quality or low complexity. In other words, in real life, you shouldn't need to configure your mocks in such a complex way.

Getting ready

Let's take a look at the following MockitoSettings interface methods:

  • extraInterfaces(...): This method specifies which additional interfaces the mock should implement. It can be quite useful when dealing with legacy code. Check out Chapter 8, Refactoring with Mockito (you shouldn't need to ever have to use it in well-written code).
  • name(...): By calling this method, you define a custom mock name. It can be useful when debugging your test since the provided name will be omnipresent in the verification errors.
  • spiedInstance(...): This method specifies the instance to spy on (refer to Chapter 3, Creating Spies and Partial Mocks, for more details on spies).
  • defaultAnswer(...): This method is used by the mock if not defined otherwise by explicit method stubbing (in other words, if you don't stub a method here, you define what should happen when you execute it).
  • serializable(): This method makes the mock serializable; however, generally speaking, you shouldn't have the need to call this method in well-designed code.
  • verboseLogging(): This method logs method invocations on the mock; it might be useful for debugging to verify which interaction was unnecessary on the mock.
  • invocationListeners(...): If you want to perform some additional debugging actions on your mocks each time a method gets executed on it, you have to implement your own listener and register it on the mock via this method.

In the following code, our system under test is a class that collects a person's IRS address and formats it properly based on his or her country:

public class TaxFactorInformationProvider {

    private final TaxService taxService;

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

    public String formatIrsAddress(Person person) {
        String irsAddress = taxService.getInternalRevenueServiceAddress(person.getCountryName());
        return "IRS:[" + irsAddress + "]";
    }

}

Let's now write a test for the system under test that will check whether the address will be properly formatted if the IRS address would be an empty string. We have to create a stub of TaxService and stub it's behavior (we don't want it to send any real requests).

How to do it...

To customize your mock's configuration via the MockitoSettings interface, you have to perform the following steps:

  1. Create the mock(Class<T> classToMock, MockSettings mockSettings) method.
  2. Pass the MockSettings object via the static withSettings() method as the second method's parameter.

Now let's take a look at the test written for JUnit. For the TestNG configuration, refer to Chapter 1, Getting Started with Mockito (I'm using the BDDMockito.given(...) and AssertJ's BDDAssertions.then(...) static methods. Check out Chapter 7, Verifying Behavior with Object Matchers, on how to work with AssertJ or how to do the same with Hamcrest's assertThat(...)).

public class MeanTaxFactorCalculatorTest {

  static final double TAX_FACTOR = 10;
  
  TaxService taxService = mock(TaxService.class, withSettings().serializable());

  MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator(taxService);

  @Test
  public void should_calculate_mean_tax_factor() {
    // given
    given(taxService.getCurrentTaxFactorFor(any(Person.class))).willReturn(TAX_FACTOR);

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

    // then
    then(meanTaxFactor).isEqualTo(TAX_FACTOR);
    then(taxService).isInstanceOf(Serializable.class);
  }
  
}

How it works...

What Mockito does internally is that it calls the MockitoCore class that is the point of entry for creating mocks. Then, a mock is created using the provided MockitoSettings object.

There's more...

If you are using annotations to create your mock, you also have some possibilities of customization. Take a look at the following @Mock annotation's additional parameters:

  • answer: This parameter is one of the predefined answers present in the Answers enum
  • name: This parameter is the name of the mock
  • extraInterfaces: This parameter specifies which additional interfaces the mock should implement

The following is an example of a @Mock annotated field containing all of the previously mentioned parameters (the probability that you will use more than one parameter, if any, is very small):

@Mock(answer = Answers.RETURNS_SMART_NULLS, extraInterfaces = {Iterable.class, Serializable.class}, name = "Custom tax service mock")
    TaxService taxService;

See also