Introduction to Mockito Integration
Integrating Mockito with JUnit 5 requires the mockito-junit-jupiter library. This enables the use of JUnit 5 extensions for Mockito annotations. In Spring Boot environments (version 2.4.0 and later), the spring-boot-starter-test starter dependency includes these libraries automatically. For standalone setups, the following Maven dependencies are required:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
Mock vs. Spy Objects
Mockito provides two primary mechanisms for creating test doubles: Mocks and Spies. A Mock is a dummy object that creates a proxy for the class, defaulting all method calls to returning neutral values (like null, 0, or false) unless explicitly stubbed. A Spy, conversely, wraps a real instance. By default, it calls the real methods of the object unless a specific behavior is stubbed. Internally, Mockito considers a Spy to be a specialized type of Mock.
Initializing Test Doubles
Using Annotations
The most concise method utilizes the @ExtendWith(MockitoExtension.class) annotation on the test class, combined with field annotations like @Mock and @Spy.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.mockingDetails;
@ExtendWith(MockitoExtension.class)
class DataHandlerTest {
@Mock
private DataRepository mockRepo;
@Spy
private DataRepository spyRepo;
@Test
void verifyTypes() {
System.out.println("Is Mock object? " + mockingDetails(mockRepo).isMock()); // true
System.out.println("Is Spy object? " + mockingDetails(mockRepo).isSpy()); // false
System.out.println("Is Spy also Mock? " + mockingDetails(spyRepo).isMock()); // true
System.out.println("Is Spy object? " + mockingDetails(spyRepo).isSpy()); // true
}
}
Programmatic Initialization
Test doubles can also be created explicitly within a setup method or test case using static methods.
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
@ExtendWith(MockitoExtension.class)
class DataHandlerManualTest {
private DataRepository manualMock;
private DataRepository manualSpy;
@BeforeEach
void init() {
manualMock = Mockito.mock(DataRepository.class);
manualSpy = Mockito.spy(DataRepository.class);
}
@Test
void verifyManualTypes() {
System.out.println("Manual Mock Check: " + mockingDetails(manualMock).isMock());
System.out.println("Manual Spy Check: " + mockingDetails(manualSpy).isSpy());
}
}
Annotation-Based Manual Init
If the MockitoExtension is not used or manual control is needed over annotated fields (@Mock, @Spy), MockitoAnnotations.openMocks(this) can be called in a lifecycle method.
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockAnnotations;
import org.mockito.Spy;
public class AnnotationInitTest {
@Spy
private DataRepository repository;
@BeforeEach
void setUp() {
// Initializes fields annotated with @Mock, @Spy, etc.
MockitoAnnotations.openMocks(this);
}
@Test
void testSpy() {
// Test logic here
}
}
Argument Matchers
Mockito allows flexible argument matching using ArgumentMatchers. This is useful when the exact argument value is not important for the verification or stubbing logic. The any() method matches any type or value, while any(Class) restricts the match to a specific type.
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@Test
void argumentMatchingExample() {
// Stub the method to return a value regardless of the String argument passed
doReturn(new DataEntity()).when(mockRepo).fetchData(any(String.class));
}
Method Stubbing
Stubbing defines the behavior of a mock method when it is invoked. This isolates the test code from external dependencies or complex logic.
Return Value Stubbing
The traditional syntax when(mock.method()).thenReturn(value) is widely used. For chaining multiple return values for consecutive calls, you can pass multiple arguments to thenReturn or chain them fluently.
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Test
void chainedStubbing() {
// Define behavior for the 1st, 2nd, and 3rd calls
when(mockRepo.getCount(any()))
.thenReturn(10)
.thenReturn(20)
.thenReturn(30);
// Alternatively: when(mockRepo.getCount(any())).thenReturn(10, 20, 30);
assertEquals(10, mockRepo.getCount("test"));
assertEquals(20, mockRepo.getCount("test"));
assertEquals(30, mockRepo.getCount("test"));
}
Exception Stubbing
To simulate error conditions, methods can be stubbed to throw exceptions. The doThrow() syntax is preferred, especially for Spy objects or void methods.
import static org.mockito.Mockito.doThrow;
@Test
void exceptionStubbing() {
doThrow(new RuntimeException("Database failure")).when(mockRepo).fetchData(any());
}
Invoking Real Methods
When using Spies, or occasionally with Mocks, you may wish to execute the actual implementation of a method. This is achieved using thenCallRealMethod() or doCallRealMethod().
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.spy;
@Test
void realMethodExecution() {
DataRepository realRepo = new DataRepository();
DataRepository partialMock = spy(realRepo);
// Call the actual method implementation
when(partialMock.getInternalState()).thenCallRealMethod();
}