1. Introduction
Mockito is one of the most popular mocking frameworks used in Java applications for effective unit testing. The main feature of this framework is defining mocked and spied objects. In this article, we will cover @Mock annotation that is an alternative for the Mockito.mock() method.
2. Enable Mockito Annotations
Mockito annotations in unit tests could be enabled using one of the following ways:
by marking test class with @RunWith(MockitoJUnitRunner.class) annotation:
@RunWith(MockitoJUnitRunner.class) public class MockAnnotationUnitTest { @Mock SampleService sampleService; }
by calling the MockitoAnnotations.initMocks() method explicitly in set up method:
public class MockAnnotationUnitTest { @Mock SampleService sampleService; @Before public void setUp() { MockitoAnnotations.initMocks(this); } }
by using dedicated MockitoRule:
public class MockAnnotationUnitTest { @Rule public MockitoRule rule = MockitoJUnit.rule(); @Mock SampleService sampleService; }
3. Creating mock objects
Let's start with a simple definition of a mock object - it is a dummy implementation for a real interface or a class in which you can define the return values for specific method calls. Mock objects usually are configured in tests to perform a particular behavior.
Mockito provides two methods to create mock objects:
- using the static Mockito.mock() method,
- using the @Mock annotation.
To use @Mock, first, we need to enable Mockito annotations - methods to do that were described in point 2.
Mockito can be used in conjunction with the JUnit framework, so our example tests will be based on that library:
package com.frontbackend.libraries.mockito;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MockitoMockTest {
interface MyDatabaseConnection {
boolean openConnection();
int update(String sql);
}
class MyService {
private final MyDatabaseConnection myDatabaseConnection;
MyService(MyDatabaseConnection myDatabaseConnection) {
this.myDatabaseConnection = myDatabaseConnection;
this.myDatabaseConnection.openConnection();
}
public boolean updateRows(String query) {
int updatedRows = myDatabaseConnection.update(query);
return updatedRows > 0;
}
}
@Mock
MyDatabaseConnection myDatabaseConnection;
@Test
public void shouldSuccessfullyUpdateRows() {
// Given
MyService myService = new MyService(myDatabaseConnection);
when(myDatabaseConnection.update(anyString())).thenReturn(10);
// When
boolean success = myService.updateRows("update table set col = 'val'");
// Then
verify(myDatabaseConnection, times(1)).openConnection();
verify(myDatabaseConnection).update("update table set col = 'val'");
Assert.assertTrue(success);
}
}
What happens in the above code:
Initialization and configuration:
@RunWith(MockitoJUnitRunner.class)
- initializing Mockito annotations,@Mock MyDatabaseConnection myDatabaseConnection;
- tells Mockito to mock the MyDatabaseConnection class,MyService myService = new MyService(myDatabaseConnection);
- instantiates the MyService class using the created mock object (notice that instead of this we could use @InjectMocks annotation),when(myDatabaseConnection.update(anyString())).thenReturn(10);
- configure the return value of mocked MyDatabaseConnection object,
Calling the test method:
boolean success = myService.updateRows("update table set col = 'val'");
- executing updateRows method from tested object,
Assertions:
verify(myDatabaseConnection, times(1)).openConnection();
- checking if openConnection() was called on mocked object once,verify(myDatabaseConnection).update("update table set col = 'val'");
- checking if the update method was called on mocked object,Assert.assertTrue(success)
- checking if the called method from test object returned true.
As you can see we can create a mock instance of an object regardless of whether it is an interface or class. It possible to stub method calls to that mock using the Mockito.when(x).thenReturn(y)
notation.
4. Configuring mock objects
Mocks can act differently depending on input parameters. To configure the behavior of the methods in mocked objects we could use when(...).then(...)
syntax. Methods like anyString()
, anyInt()
, any(class)
could be used to define that depends on the input parameter type a specific value should be returned. The when(...).thenThrow(...)
syntax is used to throw an exception when special conditions are met.
Let's extend our test class with a few more tests:
package com.frontbackend.libraries.mockito;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MockitoMockTest {
interface MyDatabaseConnection {
boolean openConnection();
int update(String sql);
}
class MyService {
private final MyDatabaseConnection myDatabaseConnection;
MyService(MyDatabaseConnection myDatabaseConnection) {
this.myDatabaseConnection = myDatabaseConnection;
this.myDatabaseConnection.openConnection();
}
public boolean updateRows(String query) {
int updatedRows = myDatabaseConnection.update(query);
return updatedRows > 0;
}
}
@Mock
MyDatabaseConnection myDatabaseConnection;
@Test
public void shouldReturnCorrectUpdatedRowsNumber() {
// Given
when(myDatabaseConnection.update(anyString())).thenReturn(10);
// When
int rows = myDatabaseConnection.update("update all");
// Then
Assert.assertEquals(10, rows);
}
@Test(expected = RuntimeException.class)
public void shouldThrowAnException() {
// Given
when(myDatabaseConnection.openConnection()).thenThrow(new RuntimeException("Connection cannot be opened!"));
MyService myService = new MyService(myDatabaseConnection);
// When
myService.updateRows("update table set col = 'val'");
}
@Test
public void shouldReturnDifferentValueDependentOnMethodParameter() {
// Given
when(myDatabaseConnection.update("update table1")).thenReturn(10);
when(myDatabaseConnection.update("update table2")).thenReturn(20);
MyService myService = new MyService(myDatabaseConnection);
// When
int rows1 = myDatabaseConnection.update("update table1");
int rows2 = myDatabaseConnection.update("update table2");
// Then
Assert.assertEquals(10, rows1);
Assert.assertEquals(20, rows2);
}
}
We can use the verify()
method on the mock object to check if the method has been called with the specific parameters and a certain number of times. That information is available because Mockito monitors all the methods calls and their parameters to the mock object.
5. Conclusion
In this article, we presented how to configure mock objects in Mockito tests and verify the behavior of the test class. With the Mockito framework, we can write tests for classes using external systems (like database, filesystem) easily.
As usual code used in this tutorial is available in our GitHub repository.
{{ 'Comments (%count%)' | trans {count:count} }}
{{ 'Comments are closed.' | trans }}