Mockito for Mocking in Java

Mockito is the most popular mocking framework for Java unit tests. It allows you to create and configure mock objects to test components in isolation.

1. Mockito Setup and Dependencies

Maven Dependencies

<!-- JUnit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<!-- Mockito -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<!-- Mockito JUnit 5 Integration -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>

Gradle Dependencies

testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
testImplementation 'org.mockito:mockito-core:5.3.1'
testImplementation 'org.mockito:mockito-junit-jupiter:5.3.1'

2. Basic Mocking with Mockito

Creating Mocks

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class BasicMockingTest {
// Different ways to create mocks
@Mock
private List<String> mockedList; // Using annotation
@Test
void testBasicMocking() {
// Using mock() method
List<String> mockList = mock(List.class);
// Configure mock behavior
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenReturn("second");
when(mockList.size()).thenReturn(100);
// Verify mock behavior
assertEquals("first", mockList.get(0));
assertEquals("second", mockList.get(1));
assertEquals(100, mockList.size());
// Default values for non-stubbed methods
assertNull(mockList.get(999)); // Returns null for non-stubbed calls
}
@Test
void testMockWithAnnotation() {
// @Mock annotated field is automatically initialized
when(mockedList.size()).thenReturn(50);
assertEquals(50, mockedList.size());
}
@Test
void testVoidMethods() {
List<String> mockList = mock(List.class);
// For void methods, use doNothing(), doThrow(), etc.
doNothing().when(mockList).clear();
doThrow(new RuntimeException("Clear failed")).when(mockList).add(anyString());
// This won't throw exception
mockList.clear();
// This will throw exception
assertThrows(RuntimeException.class, () -> mockList.add("test"));
}
}

3. Stubbing Method Calls

Different Stubbing Approaches

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class StubbingTest {
@Mock
private List<String> mockList;
@Test
void testBasicStubbing() {
// Stub with specific return value
when(mockList.get(0)).thenReturn("first");
when(mockList.get(1)).thenReturn("second");
assertEquals("first", mockList.get(0));
assertEquals("second", mockList.get(1));
}
@Test
void testConsecutiveCalls() {
// Different return values for consecutive calls
when(mockList.size())
.thenReturn(1)
.thenReturn(2)
.thenReturn(3);
assertEquals(1, mockList.size());
assertEquals(2, mockList.size());
assertEquals(3, mockList.size());
assertEquals(3, mockList.size()); // Last value repeats
}
@Test
void testStubbingWithExceptions() {
// Stub to throw exception
when(mockList.get(anyInt())).thenThrow(new RuntimeException("Database error"));
assertThrows(RuntimeException.class, () -> mockList.get(0));
assertThrows(RuntimeException.class, () -> mockList.get(1));
}
@Test
void testStubbingWithAnswer() {
// Custom logic for stubbed method
when(mockList.get(anyInt())).thenAnswer(invocation -> {
int index = invocation.getArgument(0);
return "element-" + index;
});
assertEquals("element-0", mockList.get(0));
assertEquals("element-5", mockList.get(5));
}
@Test
void testStubbingVoidMethods() {
// For void methods, use do...when pattern
doThrow(new RuntimeException("Clear failed")).when(mockList).clear();
assertThrows(RuntimeException.class, () -> mockList.clear());
}
@Test
void testStubbingWithRealMethodCall() {
List<String> realList = Arrays.asList("A", "B", "C");
List<String> spyList = spy(realList); // Spy on real object
// Stub some methods, call real methods for others
when(spyList.size()).thenReturn(100);
doReturn("X").when(spyList).get(0);
assertEquals("X", spyList.get(0)); // Stubbed
assertEquals("B", spyList.get(1)); // Real method
assertEquals(100, spyList.size()); // Stubbed
}
}

4. Argument Matchers

Using Argument Matchers

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class ArgumentMatchersTest {
@Mock
private List<String> mockList;
@Mock
private Map<String, String> mockMap;
@Test
void testBasicArgumentMatchers() {
// any(), anyString(), anyInt(), etc.
when(mockList.get(anyInt())).thenReturn("element");
when(mockMap.put(anyString(), anyString())).thenReturn("oldValue");
assertEquals("element", mockList.get(0));
assertEquals("element", mockList.get(999));
assertEquals("oldValue", mockMap.put("key", "value"));
}
@Test
void testSpecificMatchers() {
// eq() for specific values
when(mockList.set(eq(0), eq("newValue"))).thenReturn("oldValue");
// startsWith(), endsWith(), contains() for strings
when(mockMap.containsKey(startsWith("test"))).thenReturn(true);
assertEquals("oldValue", mockList.set(0, "newValue"));
assertTrue(mockMap.containsKey("test123"));
}
@Test
void testCustomMatchers() {
// Custom argument matcher
when(mockList.get(argThat(index -> index % 2 == 0))).thenReturn("even");
when(mockList.get(argThat(index -> index % 2 != 0))).thenReturn("odd");
assertEquals("even", mockList.get(0));
assertEquals("odd", mockList.get(1));
assertEquals("even", mockList.get(2));
}
@Test
void testArgumentCaptor() {
// Capture arguments for verification
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
mockList.add("test");
mockList.add("another");
// Verify and capture arguments
verify(mockList, times(2)).add(captor.capture());
List<String> allValues = captor.getAllValues();
assertEquals(2, allValues.size());
assertEquals("test", allValues.get(0));
assertEquals("another", allValues.get(1));
}
@Test
void testMixedMatchers() {
// Mix specific values and matchers
when(mockList.set(anyInt(), eq("specific"))).thenReturn("old");
assertEquals("old", mockList.set(0, "specific"));
assertEquals("old", mockList.set(999, "specific"));
// Verify with mixed matchers
verify(mockList, times(2)).set(anyInt(), eq("specific"));
}
}

5. Verification with Mockito

Verifying Mock Interactions

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class VerificationTest {
@Mock
private List<String> mockList;
@Test
void testBasicVerification() {
// Perform actions
mockList.add("one");
mockList.add("two");
mockList.clear();
// Verify interactions
verify(mockList).add("one");
verify(mockList).add("two");
verify(mockList).clear();
// Verify number of invocations
verify(mockList, times(2)).add(anyString());
verify(mockList, times(1)).clear();
// Verify no more interactions
verifyNoMoreInteractions(mockList);
}
@Test
void testVerificationModes() {
mockList.add("test");
mockList.add("test");
mockList.add("another");
// Different verification modes
verify(mockList, times(3)).add(anyString()); // Exactly 3 times
verify(mockList, atLeast(2)).add("test");    // At least 2 times
verify(mockList, atLeastOnce()).add("another"); // At least once
verify(mockList, atMost(5)).add(anyString());   // At most 5 times
// Verify no interactions
verify(mockList, never()).remove(anyString());
}
@Test
void testVerificationInOrder() {
// Verify calls in specific order
mockList.add("first");
mockList.add("second");
mockList.clear();
InOrder inOrder = inOrder(mockList);
inOrder.verify(mockList).add("first");
inOrder.verify(mockList).add("second");
inOrder.verify(mockList).clear();
}
@Test
void testVerificationWithTimeout() {
// Useful for asynchronous code
new Thread(() -> {
try {
Thread.sleep(100);
mockList.add("async");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// Verify within timeout
verify(mockList, timeout(500)).add("async");
}
@Test
void testVerificationOfZeroInteractions() {
// Verify no interactions with mock
verifyNoInteractions(mockList);
mockList.size(); // This would make the test fail if uncommented
}
}

6. Real-World Testing Examples

Service Layer Testing

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
// Domain classes
class User {
private Long id;
private String name;
private String email;
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getters and setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
// Repository interface
interface UserRepository {
Optional<User> findById(Long id);
List<User> findAll();
User save(User user);
void deleteById(Long id);
boolean existsByEmail(String email);
}
// Service class
class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User createUser(String name, String email) {
if (userRepository.existsByEmail(email)) {
throw new IllegalArgumentException("Email already exists: " + email);
}
User user = new User(null, name, email);
User savedUser = userRepository.save(user);
// Send welcome email
emailService.sendWelcomeEmail(savedUser.getEmail(), savedUser.getName());
return savedUser;
}
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found with id: " + id));
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public void deleteUser(Long id) {
if (!userRepository.findById(id).isPresent()) {
throw new RuntimeException("User not found with id: " + id);
}
userRepository.deleteById(id);
}
}
// Email service interface
interface EmailService {
void sendWelcomeEmail(String email, String name);
void sendNotification(String email, String message);
}
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void testCreateUser_Success() {
// Setup
String name = "John Doe";
String email = "[email protected]";
User savedUser = new User(1L, name, email);
when(userRepository.existsByEmail(email)).thenReturn(false);
when(userRepository.save(any(User.class))).thenReturn(savedUser);
// Execute
User result = userService.createUser(name, email);
// Verify
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals(name, result.getName());
assertEquals(email, result.getEmail());
verify(userRepository).existsByEmail(email);
verify(userRepository).save(any(User.class));
verify(emailService).sendWelcomeEmail(email, name);
}
@Test
void testCreateUser_EmailExists() {
// Setup
String name = "John Doe";
String email = "[email protected]";
when(userRepository.existsByEmail(email)).thenReturn(true);
// Execute & Verify
IllegalArgumentException exception = assertThrows(
IllegalArgumentException.class,
() -> userService.createUser(name, email)
);
assertEquals("Email already exists: " + email, exception.getMessage());
verify(userRepository).existsByEmail(email);
verify(userRepository, never()).save(any(User.class));
verify(emailService, never()).sendWelcomeEmail(anyString(), anyString());
}
@Test
void testGetUserById_Found() {
// Setup
Long userId = 1L;
User user = new User(userId, "John Doe", "[email protected]");
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
// Execute
User result = userService.getUserById(userId);
// Verify
assertNotNull(result);
assertEquals(userId, result.getId());
verify(userRepository).findById(userId);
}
@Test
void testGetUserById_NotFound() {
// Setup
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
// Execute & Verify
RuntimeException exception = assertThrows(
RuntimeException.class,
() -> userService.getUserById(userId)
);
assertEquals("User not found with id: " + userId, exception.getMessage());
verify(userRepository).findById(userId);
}
@Test
void testGetAllUsers() {
// Setup
List<User> users = Arrays.asList(
new User(1L, "John Doe", "[email protected]"),
new User(2L, "Jane Smith", "[email protected]")
);
when(userRepository.findAll()).thenReturn(users);
// Execute
List<User> result = userService.getAllUsers();
// Verify
assertEquals(2, result.size());
verify(userRepository).findAll();
}
@Test
void testDeleteUser_Success() {
// Setup
Long userId = 1L;
User user = new User(userId, "John Doe", "[email protected]");
when(userRepository.findById(userId)).thenReturn(Optional.of(user));
// Execute
userService.deleteUser(userId);
// Verify
verify(userRepository).findById(userId);
verify(userRepository).deleteById(userId);
}
@Test
void testDeleteUser_NotFound() {
// Setup
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
// Execute & Verify
RuntimeException exception = assertThrows(
RuntimeException.class,
() -> userService.deleteUser(userId)
);
assertEquals("User not found with id: " + userId, exception.getMessage());
verify(userRepository).findById(userId);
verify(userRepository, never()).deleteById(anyLong());
}
}

7. Advanced Mockito Features

Spies, Partial Mocks, and Deep Stubs

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class AdvancedMockitoTest {
@Test
void testSpyOnRealObject() {
// Spy on real object - partial mocking
List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList);
// You can stub some methods and call real methods for others
doReturn(100).when(spyList).size();
// Real method call
spyList.add("real element");
assertEquals(1, realList.size()); // Real list has one element
assertEquals(100, spyList.size()); // Stubbed method
verify(spyList).add("real element");
verify(spyList).size();
}
@Test
void testMockWithDeepStubs() {
// Deep stubbing for method chains
List<List<String>> deepMock = mock(List.class, RETURNS_DEEP_STUBS);
when(deepMock.get(0).get(0)).thenReturn("deep value");
assertEquals("deep value", deepMock.get(0).get(0));
}
@Test
void testResetMock() {
List<String> mockList = mock(List.class);
when(mockList.size()).thenReturn(10);
assertEquals(10, mockList.size());
// Reset mock
reset(mockList);
// Now returns default value
assertEquals(0, mockList.size());
}
@Test
void testMockWithDefaultAnswer() {
// Custom default behavior
List<String> mockList = mock(List.class, invocation -> {
if (invocation.getMethod().getReturnType() == int.class) {
return 42; // Default return for int methods
}
return null; // Default for other methods
});
assertEquals(42, mockList.size());
assertNull(mockList.get(0));
}
}
// Testing with complex dependencies
class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
private final NotificationService notificationService;
public OrderService(PaymentService paymentService, 
InventoryService inventoryService,
NotificationService notificationService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
this.notificationService = notificationService;
}
public Order processOrder(Order order) {
// Check inventory
if (!inventoryService.isAvailable(order.getProductId(), order.getQuantity())) {
throw new RuntimeException("Product not available");
}
// Process payment
PaymentResult paymentResult = paymentService.processPayment(
order.getCustomerId(), order.getAmount());
if (!paymentResult.isSuccess()) {
throw new RuntimeException("Payment failed: " + paymentResult.getErrorMessage());
}
// Update inventory
inventoryService.reserveProduct(order.getProductId(), order.getQuantity());
// Send notification
notificationService.sendOrderConfirmation(order.getCustomerId(), order.getId());
order.setStatus(OrderStatus.CONFIRMED);
return order;
}
}
// Supporting classes for OrderService test
class Order {
private String id;
private String customerId;
private String productId;
private int quantity;
private double amount;
private OrderStatus status;
// Constructors, getters, setters
public Order(String id, String customerId, String productId, int quantity, double amount) {
this.id = id;
this.customerId = customerId;
this.productId = productId;
this.quantity = quantity;
this.amount = amount;
}
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public double getAmount() { return amount; }
public void setAmount(double amount) { this.amount = amount; }
public OrderStatus getStatus() { return status; }
public void setStatus(OrderStatus status) { this.status = status; }
}
enum OrderStatus {
PENDING, CONFIRMED, SHIPPED, DELIVERED
}
class PaymentResult {
private boolean success;
private String errorMessage;
public PaymentResult(boolean success, String errorMessage) {
this.success = success;
this.errorMessage = errorMessage;
}
public boolean isSuccess() { return success; }
public String getErrorMessage() { return errorMessage; }
}
interface PaymentService {
PaymentResult processPayment(String customerId, double amount);
}
interface InventoryService {
boolean isAvailable(String productId, int quantity);
void reserveProduct(String productId, int quantity);
}
interface NotificationService {
void sendOrderConfirmation(String customerId, String orderId);
}
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private PaymentService paymentService;
@Mock
private InventoryService inventoryService;
@Mock
private NotificationService notificationService;
@InjectMocks
private OrderService orderService;
@Test
void testProcessOrder_Success() {
// Setup
Order order = new Order("123", "customer1", "product1", 2, 100.0);
PaymentResult paymentResult = new PaymentResult(true, null);
when(inventoryService.isAvailable("product1", 2)).thenReturn(true);
when(paymentService.processPayment("customer1", 100.0)).thenReturn(paymentResult);
// Execute
Order result = orderService.processOrder(order);
// Verify
assertEquals(OrderStatus.CONFIRMED, result.getStatus());
// Verify interactions
verify(inventoryService).isAvailable("product1", 2);
verify(paymentService).processPayment("customer1", 100.0);
verify(inventoryService).reserveProduct("product1", 2);
verify(notificationService).sendOrderConfirmation("customer1", "123");
}
@Test
void testProcessOrder_InventoryUnavailable() {
// Setup
Order order = new Order("123", "customer1", "product1", 2, 100.0);
when(inventoryService.isAvailable("product1", 2)).thenReturn(false);
// Execute & Verify
RuntimeException exception = assertThrows(
RuntimeException.class,
() -> orderService.processOrder(order)
);
assertEquals("Product not available", exception.getMessage());
verify(inventoryService).isAvailable("product1", 2);
verify(paymentService, never()).processPayment(anyString(), anyDouble());
verify(inventoryService, never()).reserveProduct(anyString(), anyInt());
verify(notificationService, never()).sendOrderConfirmation(anyString(), anyString());
}
@Test
void testProcessOrder_PaymentFailed() {
// Setup
Order order = new Order("123", "customer1", "product1", 2, 100.0);
PaymentResult paymentResult = new PaymentResult(false, "Insufficient funds");
when(inventoryService.isAvailable("product1", 2)).thenReturn(true);
when(paymentService.processPayment("customer1", 100.0)).thenReturn(paymentResult);
// Execute & Verify
RuntimeException exception = assertThrows(
RuntimeException.class,
() -> orderService.processOrder(order)
);
assertEquals("Payment failed: Insufficient funds", exception.getMessage());
verify(inventoryService).isAvailable("product1", 2);
verify(paymentService).processPayment("customer1", 100.0);
verify(inventoryService, never()).reserveProduct(anyString(), anyInt());
verify(notificationService, never()).sendOrderConfirmation(anyString(), anyString());
}
}

8. Best Practices and Common Patterns

Test Configuration Class

import org.mockito.Mockito;
// Reusable test configurations
class TestConfig {
static UserRepository createUserRepositoryMock() {
UserRepository mockRepo = Mockito.mock(UserRepository.class);
// Common stubbing
when(mockRepo.existsByEmail("[email protected]")).thenReturn(true);
when(mockRepo.existsByEmail("[email protected]")).thenReturn(false);
return mockRepo;
}
static EmailService createEmailServiceMock() {
EmailService mockService = Mockito.mock(EmailService.class);
// Verify common interactions
doNothing().when(mockService).sendWelcomeEmail(anyString(), anyString());
return mockService;
}
}
// Using static imports for cleaner tests
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
class CleanTestExample {
@Test
void testCleanSyntax() {
// Given
List<String> mockList = mock(List.class);
when(mockList.get(anyInt())).thenReturn("value");
// When
String result = mockList.get(0);
// Then
assertEquals("value", result);
verify(mockList).get(0);
}
}

Key Mockito Features Summary

  1. Mock Creation - mock(), @Mock, spy()
  2. Stubbing - when().thenReturn(), doReturn().when()
  3. Verification - verify(), times(), never()
  4. Argument Matchers - any(), eq(), argThat()
  5. Argument Captors - Capture and verify arguments
  6. InOrder Verification - Verify call sequences
  7. Spies - Partial mocking of real objects
  8. Answer Interface - Custom stubbing logic

Mockito helps create clean, maintainable tests by isolating the code under test and verifying interactions with dependencies.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper