Mockito—Creating Mocks
To begin creating mocks, let us revisit the definition we gave for Mocks in the first article.
A mock is an object that has predefined answers to method executions made during the test and has recorded expectations of these executions.
Creating a Mock
A mock should be created before it is actually used, just like anything else. Mockito provide us with various versions of thee Mockito.mock method which help us to do the same.
- mock(Class<T> classToMock): This method creates a mock of a given class with a default answer set to returning default values. This is the most used method in tests.
- mock(Class<T> classToMock, String name): This method creates a mock of a given class with a default answer set to returning default values. It also sets a name to the mock. This name is present in all verification messages. That's very useful in debugging, since it allows you to distinguish the mocks.
- mock(Class<T> classToMock, Answer defaultAnswer): This method creates a mock of a given class. In other words, all of the nonstubbed mock's methods will act as defined in the passed answer.
- mock(Class<T> classToMock, MockSettings mockSettings): This method creates a mock of a given class with customizable mock settings. You should hardly ever need to use that feature.
Getting Started
In our last article we mentioned a system which we wrote the test for. We are going to use the same class here. Here it is:
package com.discoversdk;
import com.discoversdk.model.User;
import com.discoversdk.service.SlabService;
public class MeanFactorCalculator {
private SlabService slabService;
public MeanFactorCalculator(SlabService slabService) {
this.slabService = slabService;
}
public double calculateMeanSlabFor(User user) {
double currentSlabFactor = slabService.getCurrentFactorFor(user);
double anotherSlabFactor = slabService.getCurrentFactorFor(user);
return (currentSlabFactor + anotherSlabFactor) / 2;
}
}
Let's now write a test for the system that will check whether it can properly calculate the mean value of the slab factor.
Mocking with annotations
Since Mockito integrates very nicely with JUnit , let's remove the unnecessary code and make the test more readable.
To do that, you have to perform the following steps:
- Annotate your test with @RunWith(MockitoJUnitRunner.class).
- Define the objects that you would like to mock.
- Annotate those dependencies with @Mock annotation.
Of course, this JUnit approach will work only if you haven't already annotated your test class with another @RunWith annotation.
@RunWith(MockitoJUnitRunner.class)
public class MeanTaxFactorCalculatorTest {
static final double SLAB_FACTOR = 10;
@Mock SlabService slabService;
@InjectMocks MeanFactorCalculator systemUnderTest;
@Test
public void should_calculate_mean_slab_factor() {
// given
given(slabService.getCurrentSlabFactorFor(any(User.class))).willReturn(SLAB_FACTOR);
// when
double meanSlabFactor = systemUnderTest.calculateMeanSlabFor(new User());
// then
then(meanSlabFactor).isEqualTo(SLAB_FACTOR);
}
}
Mocking with Annotation with different default answers
Usually, you won’t 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 point:
- It is probable that for debugging the app, you would like to log the arguments that were passed to the stubbed method.
- You want to perform some business logic on the passed argument rather than just return some fixed value.
- You want to stub asynchronous methods that have callbacks.
If you thought it over and still want to create a custom answer, please check if there isn't one that already exists 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
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 {
SlabService slabService = mock(SlabService.class, new ThrowsExceptionClass(IllegalStateException.class));
MeanFactorCalculator systemUnderTest = new MeanFactorCalculator(slabService);
@Test
public void should_throw_exception_when_calculating_mean_slab_factor() {
// expect
try {
systemUnderTest.calculateMeanSlabFor(new User());
fail("Should throw exception");
} catch (IllegalStateException exception) {}
}
}
Under the hood
Here is the list of additional, under the hood Mockito Answer implementations together with a short description:
- 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.
Hope you enjoyed this article. Check back soon for more!
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment