Mockito is a Java mocking framework that creates test doubles, which replace real dependencies in a unit test and record how the code under test calls them. We use Mockito with JUnit to test a class in isolation, for example a service without its database access class. In a Mockito test, we replace the database access class with a mock and verify the calls made to it. The following example injects a mock ItemRepository into ItemService with the annotations @Mock and @InjectMocks.
@ExtendWith(MockitoExtension.class)
public class ItemServiceTest {
@Mock
private ItemRepository itemRepository;
@InjectMocks
private ItemService itemService; // Assuming ItemService uses ItemRepository
@Test
public void testCreateItem() {
// ...
}
}
1. Introduction to Mockito
Mockito is an open-source framework that creates test doubles (mocks). A test double is any object that replaces a production object in a test.
Mockito works with the following kinds of test doubles.
- Stubs are objects with predefined return values for the method calls made during the test.
- Spies are stubs that also record how they were called.
- Mocks are objects with return values for the method calls made during the test, and with recorded expectations for these calls. A mock throws an exception when it receives an unexpected call, and verification checks that it received all the expected calls.
Mockito can mock both interfaces and classes in a test class. The Mockito annotations reduce the boilerplate code for creating the mocks.
Once created, a mock records all interactions, and the test verifies the interactions it needs.
2. Mockito Setup with JUnit
2.1. Maven
To add Mockito to the project, we add the latest Mockito version with Maven, Gradle or a jar file.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
testCompile group: 'org.mockito', name: 'mockito-core', version: '4.6.1'
2.2. Bootstrapping with JUnit 5
To use Mockito with JUnit 5, add the JUnit Jupiter dependency.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
To process the Mockito annotations with JUnit 5, we register the extension class MockitoExtension with @ExtendWith.
@ExtendWith(MockitoExtension.class)
public class ApplicationTest {
//code
}
2.2. Bootstrapping with JUnit 4 [Legacy]
To use Mockito with JUnit 4, add the JUnit 4 dependency.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
JUnit 4 tests use either the runner class MockitoJUnitRunner or the rule class MockitoRule.
@RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {
//code
}
@RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {
@Rule
public MockitoRule rule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
//code
}
Strict stubbing reduces duplicated test code and makes failures easier to debug. The test fails early when the code under test calls a stubbed method with different arguments, or when a stub is unused.
2.3. Programmatic Initialization
Alternatively, the method MockitoAnnotations.openMocks(), called in a base class or a test runner, initializes the fields annotated with @Mock, @Spy, @Captor and @InjectMocks.
The previously used initMocks() method is now deprecated.
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
}
3. Mockito Annotations
Mockito provides the following annotations for the test doubles.
- @Mock creates a mock. It makes the test class more readable.
- @Spy creates a spy instance and replaces the method spy(Object).
- @InjectMocks creates an instance of the tested object and injects the fields annotated with @Mock or @Spy into it, where applicable. Read about the difference between @Mock and @InitMocks annotations.
- @Captor creates an argument captor.
public class ApplicationTest {
@Mock
Depedency mock;
@InjectMocks
Service codeUnderTest;
}
4. Mockito Demo
4.1. System Under Test
The following example is a typical use case, in which the class RecordService calls the class RecordDao to save a Record.
The class RecordService uses the class SequenceGenerator to get the next record id.
4.2. Test Demo
- To test the method RecordService.saveRecord(), we inject RecordDao and SequenceGenerator into it as dependencies with the annotations @Mock and @InjectMocks.
- The annotation @ExtendWith(MockitoExtension.class) starts the bootstrapping and injects the mocks into the service instance.
- The test records the expectations of the mock objects with the methods when(…).then(…) and verifies them after the application code runs with the method verify().
- Additional JUnit assertions validate the rest of the result, if needed.
@ExtendWith(MockitoExtension.class)
public class MockitoHelloTest {
@Mock
RecordDao mockDao;
@Mock
NotificationService mockNotification;
@Mock
SequenceGenerator mockGenerator;
@InjectMocks
RecordService service;
@Test
public void testSaveRecord() {
Record record = new Record();
record.setName("Test Record");
when(mockGenerator.getNext()).thenReturn(100L);
when(mockDao.saveRecord(record)).thenReturn(record);
Record savedRecord = service.saveRecord(record);
verify(mockGenerator, times(1)).getNext();
verify(mockDao, times(1)).saveRecord(any(Record.class));
assertEquals("Test Record", savedRecord.getName());
assertEquals(100L, savedRecord.getId());
}
}
5. Conclusion
Mockito creates mocks and stubs that replace dependencies in JUnit tests. The extension class MockitoExtension initializes them in JUnit 5, and the runner and rule classes do the same in JUnit 4.
The demo test combines the annotations, the stubbing with when() and the verification with verify() in one test.
Happy Learning !!