Mockito APIs - How could you use them in unit testing
Published On: 2021/01/25
As developers, we cannot ignore the importance of writing unit testcases to ensure the code which we wrote, runs as expected for a given set of input. When you run the unit test for a particular class, you need to mock(test doubles) the functionalities of other classes that are used in the class under test. For this we need to use a mocking framework. There are a number of opensource mocking frameworks available to help java developers and one of them is Mockito. As the name implies it helps the developers to inject mock data to the executing code to verify a test scenario. In this article, I would like to cover the APIs which I have been using in my projects.
Injecting Mocks
Let us take the example of a Rating service in the cargo domain to learn how mocks could be injected to class under test. In this example the class RatingService uses TariffService to fetch the rate matrix for a given leg (source port to destination port).
public class RatingServiceBean implements RatingService{
public final TariffService tariffService;
public RatingServiceBean(TariffService tariffService) {
this.tariffService = tariffService;
}
public Rate rate(Port sourcePort, Port destinationPort, Shipment shipment, String tariffCode) {
...
Tariff tariff = this.tariffService.getTariff(tariffCode)
...
return this.calculate(shipment, tariff);
}
public Rate calculate(Shipment shipment, Tariff tariff) {
...
return rate
}
}
In this case we could inject the mock to the constructor of the RatingServiceBean in two ways.
1. A setup method with @Before junit annotation
In this example Junit could inject the mock to the constructor of the RatingServiceBean in a function annotated with its @Before annotation.
@RunWith(MockitoJUnitRunner.class)
public class RatingServiceBeanTest {
private RatingService underTest;
@Mock
private TariffService tariffService;
@Before
public void setUp() {
underTest = new RatingServiceBean(tariffService);
}
@Test
public void rate_should_be_calculated_for_the_shipment_to_DXB_from_AUT(){
}
}
2. Using @InjectMock annotation
When we are using InjectMocks annotation, the mockito would find the mocks of the attributes, which have to be passed to the constructor of the class under test, and inject those varibles to the constructor. In the below example the mock of TariffService (variable tariffService) is injected to the constructor of the RatingServiceBean.
@RunWith(MockitoJUnitRunner.class)
public class RatingServiceBeanTest {
@InjectMocks
private RatingServiceBean underTest;
@Mock
private TariffService tariffService;
@Test
public void rate_should_be_calculated_for_the_shipment_to_DXB_from_AUT(){
...
}
}
In the above two examples we have annotated the test class with @RunWith(MockitoJUnitRunner.class) in order to avoid the explicit call to the MockitoAnnotations.openMocks(Object) so that test will be more readable. The MockitoJUnitRunner class initialize the objects annotated with mockito annotations like @Mock, @Spy, @Captor and @InjectMocks.
mock static method
A Mockito mock allows you to stub a method call. Mock of a class could be created using @Mock annotation or by using the static method “mock” provided in the mockito library. The Mock annotation gives much more readability than using repeated mock static method to create mocks.
1. Using Mock annotation
public class RatingServiceBeanTest {
...
@Mock
private TariffService tariffService;
...
}
2. Using mock staic method
public class RatingServiceBeanTest {
private RatingServiceBean underTest;
@Before
public void setUp() {
TariffService tariffService = mock(TariffService.class);
underTest = new RatingServiceBean(tariffService);
}
...
}
verify
The verify is the mockito method used to check the methods of mock object called with given arguments or can use flexible argument matching (using any()) or can capture the argument passed to a method.
@RunWith(MockitoJUnitRunner.class)
public class RatingServiceBeanTest {
@InjectMocks
private RatingServiceBean underTest;
@Mock
private TariffService tariffServiceMock;
@Test
public void rate_should_be_calculated_for_the_shipment_to_DXB_from_AUT(){
String tariffCode = "TEST01";
underTest.rate(Port.DXB, Port.AUT, shipment, tariffCode):
verify(tariffServiceMock).getTariff(tariffCode);
// this call is similar to verify(tariffServiceMock, times(1)).getTariff(tariffCode);
}
}
If we want to check whether the method call is happening for a defined number of times, then we could use the times(n) argument in the verify method. Suppose, the RatingService calls the LegRateService to calculate the rate of shipment for each leg and we would like to test that the method rateLeg of the LegRateService is called exactly equal to the number of legs, we could use the times(n) attribute of verify method.
{
Shipment shipment = this.shipmentToAUTFromDXB(); // get the test data
List<Leg> legs = List.of(new Leg(Port.DXB,Port.DEU), new Leg(Port.DEU,Port.AUT));
verify(legRateServiceMock, times(2)).rateLeg(any(Leg.class),shipment);
}
Similar to times(n) we could use the arguments like never() , atLeastOnce() , atLeast(n), atMost(n) in the verify method.
never() - This argument verifies that the given method of mock is never called during the test execution. This could be used when we test the conditional block of code.
verify(mockObject, never()).someMethod("This method is never called");
// Application Code
{
n = < a dynamic value greater than or equal to 5 >
for(n=1...n) {
if(0=n%5){
tickService.tick();
}
}
}
// Test Code
{
verify(tickServiceMock, atLeastOnce()).tick();
}
Use of Captor
Captor is used to capture the argument passed to the method of a mocked object. This is vey useful to verify the argument passed to the method is what we expected to be passed.
@RunWith(MockitoJUnitRunner.class)
public class ShippingServiceBeanTest {
@InjectMocks
private ShippingServiceBean underTest;
@Mock
private RatingService ratingService;
@Captor
private ArgumentCaptor<RatingInput> ratingInputCaptor;
@Test
public void shipment_should_be_dispatched_to_DXB_from_AUT(){
...
verify(ratingService, times(1)).rate(ratingInputCaptor.capture());
...
assertThat(ratingInputCaptor.getValue().getSourcePort()).isEqualTo(Port.AUT);
assertThat(ratingInputCaptor.getValue().getDestinationPort()).isEqualTo(Port.DXB);
}
}
Spy
As the name implies, a spy in mockito spies the actual object and mocks those methods which we intented to mock. The calls to the other methods of the object goes to the real implementation. Due to this reason we could say that a spy in mockito is a partial mock. In a software project which follows the single responsibility code principle, you won’t come across the necessity of using a Spy in unit testing.
Testing Exceptions with mockito
Sometimes programmers forget to write the testcases for exception handling scenarios though it is a critical aspect of secure coding. Using Junit one could easily check whether the expected exception with appropriate message is thrown for a given input data. The annotation @Rule is used to verify the exception and the message.
@RunWith(MockitoJUnitRunner.class)
public class RatingServiceBeanTest {
private RatingService underTest;
@Mock
private TariffService tariffService;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() {
underTest = new RatingServiceBean(tariffService);
}
@Test
public void throw_exception_when_tariff_does_not_exist() {
String tariffCode = "NOTARIFF01";
Shipment shipment = this.getShipmentTestData();
when(tariffService.getTariff(tariffCode).thenReturn(Optional.empty());
thrown.expect(NoTariffFoundException.class);
thrown.expectMessage(
String.format("Tariff does not exist for the given code [%s]",tariffCode));
underTest.rate(Port.AUT, Port.DXB, shipment, tariffCode);
}
In case if your method catches an exception and returns an object that contains error data, we could test this scenario with the help of doThrow method of Mockito library.
@Test
public void should_send_failure_when_invalid_shipment_type_found() {
String messageId = UUID.randomUUID().toString();
ShipmentResponse expected = new ShipmentResponse(OUTQ, SendingStatus.FAILURE);
ShipmentRequest request = new ShipmentRequest(UUID.randomUUID(), address, ...);
doThrow(new UncategorizedJmsException("a failure note")).when(jmsTemplateMock)
.send(eq(OUTQ), any(MessageCreator.class));
ShipmentResponse response = underTest.sendMessage(messageId, request);
verify(jmsTemplateMock).send(eq(OUTQ), any(MessageCreator.class));
//uses assertj for assertion
assertThat(response).isEqualTo(expected);
}
How to Mock a void method?
How could we mock a method with void return type? Most of the programmers ask this question themselves. We could use the below ways to mock and verify a void method.
- Use the captor to check that expected values are passed to the method parameters.
- Use doAnswer to assert that the input values to the method are as expected.
- Use doNothing to tell the mock system to avoid executing the real method on mock object.
@Test
public void should_send_success_response_on_valid_input() {
String messageId = UUID.randomUUID().toString();
ShipmentResponse expected = new ShipmentResponse(OUTQ, SendingStatus.SUCCESS);
ShipmentRequest request = new ShipmentRequest(UUID.randomUUID(),address, ...);
doNothing().when(jmsTemplateMock).send(eq(OUTQ), any(MessageCreator.class));
ShipmentResponse response = underTest.sendMessage(messageId, request);
verify(jmsTemplateMock).send(eq(OUTQ), any(MessageCreator.class));
assertThat(response).isEqualTo(expected);
}
BDD with Mockito
Behavior-Driven Development, is a well known software development paradigm which focuses on how a system behaves when an action occures in a given condition / state of the system. This is represented by three human readable keywords given, when, then when writing testcases.
Mockito has released a class BDDMockito to support developers to write unit test in BDD style. In standard Mockito code what we write in when static method is written in given method of BDDMockito.
In standared Mockito
when(mock.methodCall()).thenReturn(someObject)
In BDD Mockito
given(mock.methodCall()).willReturn(someObject)
OR
given(mock.methodCall()).willAnswer(doSomething)
Let us take the previous example to write the testcase in BDD style.
@RunWith(MockitoJUnitRunner.class)
public class RatingServiceBeanTest {
private RatingService underTest;
@Mock
private TariffService tariffService;
@Before
public void setUp() {
underTest = new RatingServiceBean(tariffService);
}
@Test
public void rate_should_be_calculated_for_the_shipment_to_DXB_from_AUT(){
String tariffCode = "TEST01";
Shipment shipment = this.getShipmentTestData();
//given
given(tariffService.getTariff(tariffCode)).willReturn(this.getMockTariff());
//when
Rate rate = underTest.rate(Port.AUT, Port.DXB, shipment, tariffCode);
//then
then(rate.getAmount()).as("Check that an amount is calculated").isNotNull();
}
}
Conclusion
Creating actual unit testcases are not that difficult when we have a framework to mock apis those called from a class under test.Mockito suites this purpose. In this article, I tried to cover the Mockito methods that I have been using in the spring boot projects. The testcases and code which I have used in this article is not real code but created to explain the Mockito APIs.