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

Creating mocks with a different default answer

If not changed by the custom configuration, Mockito sets the mock ReturnsEmptyValues answer by default (for details on that answer, please check the subsequent There's more... section). Note that in Chapter 4, Stubbing Behavior of Mocks, where we deal with stubbing of particular methods, you can learn how to stub particular methods with a custom answer.

In the following recipe, we will see how to change the default answer to a custom or a predefined one.

Getting ready

It is more than probable that you will not ever need to create a custom answer for Mockito—there are plenty of them already bundled in Mockito and there is no need to reinvent the wheel. Why would you want to create a custom answer anyway? Let's take a look at a couple of possible answers to that question:

  • It is possible that for debugging purposes, you would like to log the arguments that were passed to the stubbed method
  • You could also want to perform some more complex logic on the passed argument rather than just return some fixed value
  • You want to stub asynchronous methods that have callbacks (you provide those callbacks in the custom Answer implementation)
  • Believe me, you don't want to capture the arguments and check them! Check Chapter 6, Verifying Test Doubles, for more information

If you thought it over and still want to create a custom answer, please check if there isn't one already existing in Mockito.

In the provided Mockito API, you can find the following answers in the AdditionalAnswers class (check the Javadoc of that class for examples):

  • returnsFirstArg: This answer will return the first argument of the invocation
  • returnsSecondArg: This answer returns the second argument of the invocation
  • returnsLastArg: This answer returns the last argument of the invocation
  • returnsArgAt: This answer returns the argument of the invocation provided at the given index
  • delegatesTo: This answer delegates all methods to the delegate (you will in fact call the delegate's method if the method hasn't already been stubbed)
  • returnsElementsOf: This answer returns the elements of the provided collection

There is also the Mockito class itself that contains some Answer interface implementations (they are static final fields, thus their names are in uppercase). These are RETURNS_DEFAULT, RETURNS_SMART_NULLS, RETURNS_MOCKS, RETURNS_DEEP_STUBS, and CALLS_REAL_METHODS; they all delegate to answers described in more depth in the There's more... section.

If you feel that none of these answers satisfy your requirements, you have to create your own implementation of the Answer interface. The next part of this recipe will show how to pass the answer to the created mock. Our system under test is a class that calculates a mean value of tax factors retrieved through a web service. Have a look at the following code:

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;
    }

}

How to do it...

To set a different default answer without annotations, you have to use the overloaded Mockito.mock(Class<T> classToMock, Answer defaultAnswer) static method.

The following snippet shows an example of a test that uses the ThrowsExceptionClass answer set on a mock as its default answer:

public class MeanTaxFactorCalculatorTest {
  
  TaxService taxService = mock(TaxService.class, new ThrowsExceptionClass(IllegalStateException.class));

  MeanTaxFactorCalculator systemUnderTest = new MeanTaxFactorCalculator(taxService);

  @Test
  public void should_throw_exception_when_calculating_mean_tax_factor() {
    // expect
    try {      
      systemUnderTest.calculateMeanTaxFactorFor(new Person());
      fail("Should throw exception");
    } catch (IllegalStateException exception) {}
  }
  
}

How it works...

Mockito takes the passed answer type argument and creates MockitoSettings from it as follows:

public static <T> T mock(Class<T> classToMock, Answer defaultAnswer) {
        return mock(classToMock, withSettings().defaultAnswer(defaultAnswer));
    }

In this way, the default mock's answer is changed to the custom one. In this way, if not previously stubbed, all of the mock's methods will, by default, execute the logic from the passed Answer implementation.

There's more...

Here is the list of additional, interesting Mockito Answer implementations together with a short description (mind you, they are part of the Mockito internals, so I'm presenting them for you to understand what happens under the hood while using Mockito. Be cautious when using them):

  • Returns: It always returns the object passed in the constructor of this Answer implementation.
  • ReturnsEmptyValues: When creating a mock, all of its methods are stubbed as follows based on the method's return type:
    • For primitives: It returns default Java-appropriate primitive values (0 for integer, false for boolean, and so on)
    • For primitive wrappers: It returns the same values as for primitives
    • For most commonly used collection types: It returns an empty collection
    • For the toString() method: It returns the mock's name
    • For Comparable.compareTo(T other): It returns 1 (meaning that the objects are not equal to each other)
    • For anything else: It returns null
  • ReturnsMoreEmptyValues: This implementation extends the ReturnsEmptyValues functionality with the following additional default return types:
    • For arrays: It returns an empty array
    • For strings: It returns an empty string ("")
    • Returns an empty array for methods that return arrays
    • Returns an empty string ("") for methods returning strings
  • ReturnsSmartNulls: If a NullPointerException gets thrown on mock, Mockito catches it and rethrows SmartNullPointerException with additional helpful messages. Additionally, it acts like ReturnsMoreEmptyValues.
  • DoesNothing: This method always returns null for objects (non-primitive types) and default values for primitives.
  • CallsRealMethods: This method creates a partial mock by default, unstubbed methods delegate to real implementations.
  • ReturnsArgumentAt: This method returns an argument at a specified position of an array (for -1, it returns its last element).
  • ReturnsElementsOf: This method keeps returning subsequent elements of the collection that is passed in the constructor. Once it arrives at the tail of the collection, it will always return that value.
  • ReturnsDeepStubs: This method allows easy nested mock creation and method chain stubbing. Check Chapter 8, Refactoring with Mockito, for usage examples and suggestions why you should not use it.
  • ThrowsExceptionClass: This method throws the exception passed as the argument to the constructor of Answer for each method. Mockito will instantiate the exception for you.
  • ThrowsException: This method throws an instantiated exception passed to the constructor of Answer.
  • ReturnsMocks: First, this method tries to return values such as the ones defined in ReturnsMoreEmptyValues and, if that fails, it tries to return a mock. Eventually, if this attempt fails at either of them, ReturnsMocks returns null. Please think twice before using this answer (or use it only to refactor some legacy code), since it clearly means that something is wrong with your design.

    Note

    Another interesting feature is that if you create a class called MockitoConfiguration that implements IMockitoConfiguration or extends the DefaultMockitoConfiguration class in the org.mockito.configuration package. You can then set a global answer for all your mocks. The following snippet shows what a Mockito configuration class should look like in order to change the default answer of any mock to ReturnsSmartNulls:

    public class MockitoConfiguration extends DefaultMockitoConfiguration {
    
      public Answer<Object> getDefaultAnswer() {
        return new ReturnsSmartNulls();
      }
        
    }

See also