Stubbing Behavior of Mocks (Part 2)
In the previous lesson, we learned a lot about how to stub the behavior of Mock methods. In this lesson, we will continue our journey and cover how to
- Stub methods so they return custom answers
- Stub methods so they call real methods
- Stub void methods
- Stub void methods so they throw exceptions
Stubbing methods so that they return custom answers
We will reuse the System under Test which is defined as below:
public class MeanTaxFactorCalculator {
private final TaxFactorFetcher taxFactorFetcher;
public MeanTaxFactorCalculator(TaxFactorFetcher taxFactorFetcher) {
this.taxFactorFetcher = taxFactorFetcher;
}
public float calculateMeanTaxFactorFor(User user) {
float taxFactor = taxFactorFetcher.getTaxFactorFor(user);
float anotherTaxFactor = taxFactorFetcher.getTaxFactorFor(user);
return (taxFactor + anotherTaxFactor) / 2;
}
}
To stub non-void methods so they execute the logic from the custom answer, we need to perform the following steps:
1. Call
Mockito.when(mock.methodToStub()).thenAnswer(answer)
2. Regardless of the chosen approach in the given(...) or when(...) method, we need to provide the mock's method call, and in willAnswer(...) or thenAnswer(...), we need to provide the desired Answer implementation.
3. Remember that the last passed value during the stubbing will be returned for each stubbed method call. In other words, we stub the mock as follows:
given(taxFetcher.getTax()).willAnswer(new Answer1(), new Answer2());
4. Then, regardless of the number of taxFetcher.getTax() method executions, first Answer1 will be executed, and then we will always have Answer2 executed (until it is stubbed again).
Now, let's move to the JUnit test.
@RunWith(MockitoJUnitRunner.class)
public class MeanTaxFactorCalculatorTest {
@Mock TaxFactorFetcher taxFactorFetcher;
@InjectMocks MeanTaxFactorCalculator systemUnderTest;
@Test
public void should_return_tax_factor_incremented_by_additional_factor_when_calculating_mean_tax_factor() {
// given
final float additionalTaxFactor = 100;
final float factorForUserFromUndefinedCountry = 200;
given(taxFactorFetcher.getTaxFactorFor(any(User.class))).willAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
if (invocation.getArguments().length > 0) {
User user = (User) invocation.getArguments()[0];
if (!user.isCountryDefined()) {
return additionalTaxFactor + factorForUserFromUndefinedCountry;
}
}
return additionalTaxFactor;
}
});
// when
float meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new User());
// then
then(meanTaxFactor).isEqualTo(additionalTaxFactor + factorForUserFromUndefinedCountry);
}
}
Another thing to remember is that most likely, we won't have the need to create any special answers.
It's worth mentioning that eventually, when the willThrow(...) or thenThrow(...) code is called, Mockito constructs the ThrowsException answer with the passed exception and then delegates further execution to it.
Mockito provides a series of possible answers to be executed either by using the fluent interface API, or by means of varargs.
We can perform stubbing via a fluent API as follows:
given(...).willAnswer(answer1).willAnswer(answer2)...willAnswer(answer3);
Or, with the varargs style, we can perform the stubbing as follows:
given(...).willAnswer(answer1, answer2, …. answerN);
Stubbing methods so that they call real methods
Now, we will stub a method that returns a value so that it calls a real method. This way, we will construct a partial mock.
To start, we will use the same system under test which is MeanTaxFactorCalculator.
Unlike the previous recipes, TaxFactorFetcher will not be an interface but a concrete class.
Executing logic from custom answer
To stub nonvoid methods so that they execute the logic from the custom answer, we have to perform the following steps:
1. Call
Mockito.when(mock.methodToStub()).thenCallRealMehod();
2. Regardless of the chosen approach in the given(...) or when(...) method, we need to provide the mock's method call.
3. Remember that the last passed value during the stubbing will be returned for each stubbed method call. In other words, say that we stub the mock as follows:
4. given(taxFetcher.getTax()).willReturn(2). willCallRealMethod()
5. Then, regardless of the number of taxFetcher.getTax() method executions, the first 2 will be returned, and then we will always have the real logic executed (until it is stubbed again).
Now, let's move to the JUnit test:
@RunWith(MockitoJUnitRunner.class)
public class MeanTaxFactorCalculatorTest {
@Mock TaxService taxService;
@InjectMocks MeanTaxFactorCalculator systemUnderTest;
@Test
public void should_return_mean_tax_factor() {
// given
float taxFactor = 15000;
float expectedMeanTaxFactor = (TaxService.NO_COUNTRY_TAX_FACTOR + taxFactor) / 2;
given(taxService.getTaxFactorFor(any(User.class))).willCallRealMethod().willReturn(taxFactor);
// when
float meanTaxFactor = systemUnderTest.calculateMeanTaxFactorFor(new User());
// then
then(meanTaxFactor).isEqualTo(expectedMeanTaxFactor);
}
}
Another thing to remember is that if we need to create a partial mock, and if we really don't have some strong arguments to back that decision up, then we should rethink the architecture of our program or our tests since it is not of the best quality, most likely.
Stubbing void methods
In this section, we will stub a void method that doesn't return a value. The key with void methods is that Mockito assumes that they do nothing by default, so there is no need to explicitly stub them (although we may do it).
How to do it?
If we do not want our void method to execute logic, we need to perform the following steps:
1. Do nothing: Mockito stubs the method for us so that it does nothing.
2. Call
Mockito.doNothing().when(mock).methodToStub();
3. Regardless of the chosen approach in the given(...) or when(...) method, we need to provide the mock object (and not the method call in the case of methods that return values).
4. Remember that the last passed value during the stubbing will be returned for each stubbed method call. Say that we stub the mock as follows.
willThrow(new Exception1()).willNothing().given(userSaver).saveUser(john);
5. Then, regardless of the number of userSaver.saveUser(...) method executions, first an exception will be thrown, and then we will always have no action taken (until it is stubbed again).
What Mockito does internally when we start stubbing using the methods starting with do...(...) or will...(...) is that the MockitoCore.doAnswer(...) method is executed with a proper answer, which, in the case of void methods that don't do anything, is the DoesNothing answer.
It's worth mentioning that as a result of the execution of the doAnswer(...) method, we have the Stubber interface returned, which has several fluent API methods (that return Stubber itself), for example, the following one:
Stubber doNothing();
It also provides us with a method that returns the stubbed object, the when method (BDDMockito delegates method execution to the when method as well).
<T> T when(T mock);
This is why we can profit from Mockito's fluent API, and when we call the when method, we have access to the mocked object's methods.
Stubbing void methods so that they throw exceptions
In this section, we will stub a void method that doesn't return a value, so it throws an exception.
To start, our system under test will be a UserProcessor class that, for simplicity, does only one thing: it delegates the process of saving user to the UserSaver class. As shown in the following code, in case of success, true is returned; otherwise, false is returned:
public class UserProcessor {
private final UserSaver userSaver;
public UserProcessor(UserSaver userSaver) {
this.userSaver = userSaver;
}
public boolean process(User user) {
try {
userSaver.saveUser(user);
return true;
} catch (FailedToSavedUserDataException e) {
System.err.printf("Exception occurred while trying save user data [%s]%n", e);
return false;
}
}
}
How to do it?
If we want our void method to throw an exception upon calling, we need to perform the following steps:
1. Call
Mockito.doThrow(exception).when(mock).methodToStub();
2. Regardless of the chosen approach in the given(...) or when(...) method, we need to provide the mock object (and not the method call in the case of methods that return values).
3. Remember that the last passed value during the stubbing will be returned for each stubbed method call. In other words, say that we stub the mock as follows:
willThrow(new Exception1()).willThrow(new Exception2()).given(userSaver).saveUser(john);
4. Then, regardless of the number of userSaver.saveUser(...) method executions, first Exception1 will be thrown, and then we will always have Exception2 thrown (until it is stubbed again).
Let's check the JUnit test.
@RunWith(MockitoJUnitRunner.class)
public class UserProcessorTest {
@Mock UserSaver userSaver;
@InjectMocks UserProcessor systemUnderTest;
@Test
public void should_fail_to_save_user_data_when_exception_occurs() {
// given
willThrow(FailedToSavedUserDataException.class).given(userSaver).saveUser(any(User.class));
// when
boolean updateSuccessful = systemUnderTest.process(new User());
// then
then(updateSuccessful).isFalse();
}
}
How it works?
It is important to mention that the answers that take part in the Mockito internal delegation process are either ThrowsExceptionClass or ThrowsException answers.
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