When testing, you often do not want to depend on real databases, external services, or complex objects. Mocking lets you create fake versions of these dependencies so you can test your code in isolation. Mockito is the most popular mocking library for Java, and it integrates seamlessly with JUnit 5.
Adding Mockito
<!-- Maven -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>junit-jupiter-mockito</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<!-- Or for the Mockito BOM -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
// Gradle
testImplementation 'org.mockito:mockito-core:5.11.0'
Basic Mocking
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private EmailService emailService;
@Mock
private PaymentGateway paymentGateway;
@Test
void shouldPlaceOrder() {
// Arrange
OrderService service = new OrderService(emailService, paymentGateway);
Order order = new Order("user@example.com", 99.99);
when(paymentGateway.charge(99.99)).thenReturn(true);
doNothing().when(emailService).sendConfirmation(anyString());
// Act
service.placeOrder(order);
// Assert
verify(paymentGateway).charge(99.99);
verify(emailService).sendConfirmation("user@example.com");
}
}
Creating Mocks with Mockito.mock()
class MockCreationDemo {
@Test
void mockCreation() {
// Method 1: mock() method
List<String> mockList = mock(List.class);
// Method 2: @Mock annotation (with @ExtendWith)
// (shown above)
// Use the mock
when(mockList.size()).thenReturn(5);
assertEquals(5, mockList.size());
}
}
Stubbing Methods
Stubbing means telling the mock what to return when certain methods are called:
@Test
void stubbingDemo() {
List<String> mockList = mock(List.class);
// Return a specific value
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenReturn("second");
// Return different values on successive calls
when(mockList.size())
.thenReturn(3)
.thenReturn(2)
.thenReturn(1);
assertEquals("first", mockList.get(0));
assertEquals("second", mockList.get(1));
assertEquals(3, mockList.size());
assertEquals(2, mockList.size());
assertEquals(1, mockList.size());
// Throw an exception
when(mockList.get(99)).thenThrow(
new IndexOutOfBoundsException("Index 99 out of bounds")
);
assertThrows(IndexOutOfBoundsException.class,
() -> mockList.get(99));
}
@InjectMocks
When your class under test has dependencies, use @InjectMocks to
automatically inject mocked dependencies:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService; // Dependencies injected automatically
@Test
void shouldRegisterUser() {
User user = new User("john@example.com");
when(userRepository.save(any(User.class))).thenReturn(user);
doNothing().when(emailService).sendWelcome(anyString());
userService.register(user);
verify(userRepository).save(user);
verify(emailService).sendWelcome("john@example.com");
}
}
Mock vs Spy
// Mock: completely fake, all methods return defaults
List<String> mock = mock(List.class);
mock.size(); // returns 0 (default for int)
mock.isEmpty(); // returns true (default for boolean)
// Spy: wraps real object, calls real methods unless stubbed
List<String> realList = new ArrayList<>();
realList.add("hello");
List<String> spy = spy(realList);
spy.size(); // returns 1 (real method called!)
when(spy.isEmpty()).thenReturn(true);
spy.isEmpty(); // returns true (stubbed)
When to Mock
- External services - APIs, payment gateways, email services
- Database access - Repository classes, data mappers
- File system operations - File readers, writers
- Time-dependent code - Mock the clock for deterministic tests
- Complex objects - Objects that are expensive to create