
Mockito Tutorial – Basics of Mocking in Java
Unit testing is the backbone of any solid Java application, but dealing with external dependencies like databases, web services, or complex object hierarchies can turn your tests into a nightmare. That’s where Mockito comes in – it’s the most popular mocking framework for Java that lets you create fake objects (mocks) that behave exactly how you want them to during testing. This tutorial will walk you through Mockito’s core concepts, show you how to set it up, and give you practical examples that you can start using immediately to write better, faster, and more reliable unit tests.
How Mockito Works Under the Hood
Mockito uses a combination of reflection and bytecode manipulation to create mock objects at runtime. When you create a mock, Mockito generates a proxy class that extends or implements your target class/interface. This proxy intercepts method calls and returns predefined values or executes custom logic based on your test setup.
The framework operates on three main principles:
- Stubbing – Define what methods should return when called
- Verification – Check if specific methods were called with expected parameters
- Argument matching – Use flexible matchers to handle different input scenarios
Here’s what happens when you create a mock:
// Mockito creates a proxy that intercepts calls
UserService mockService = Mockito.mock(UserService.class);
// Behind the scenes, Mockito:
// 1. Creates a proxy class extending UserService
// 2. Registers the mock in its internal registry
// 3. Sets up default return values (null for objects, 0 for primitives, false for booleans)
Step-by-Step Setup and Implementation
Getting started with Mockito is straightforward. Add the dependency to your project and you’re ready to mock.
Maven Setup
Add this to your pom.xml
:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<!-- For JUnit 5 integration -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
Gradle Setup
testImplementation 'org.mockito:mockito-core:5.5.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.5.0'
Basic Mock Creation and Usage
Let’s start with a simple example. Imagine you have a service that depends on a repository:
public class UserService {
private UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User findUserById(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("Invalid user ID");
}
return userRepository.findById(id);
}
public boolean isUserActive(Long id) {
User user = findUserById(id);
return user != null && user.isActive();
}
}
public interface UserRepository {
User findById(Long id);
}
public class User {
private Long id;
private String name;
private boolean active;
// constructors, getters, setters
public User(Long id, String name, boolean active) {
this.id = id;
this.name = name;
this.active = active;
}
public boolean isActive() { return active; }
public Long getId() { return id; }
public String getName() { return name; }
}
Now let's test this service using Mockito:
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
public class UserServiceTest {
private UserRepository mockRepository;
private UserService userService;
@BeforeEach
void setUp() {
// Create mock repository
mockRepository = mock(UserRepository.class);
userService = new UserService(mockRepository);
}
@Test
void shouldReturnUserWhenValidIdProvided() {
// Arrange
Long userId = 1L;
User expectedUser = new User(userId, "John Doe", true);
// Stub the repository method
when(mockRepository.findById(userId)).thenReturn(expectedUser);
// Act
User actualUser = userService.findUserById(userId);
// Assert
assertEquals(expectedUser, actualUser);
verify(mockRepository).findById(userId); // Verify the method was called
}
@Test
void shouldThrowExceptionWhenInvalidIdProvided() {
// Act & Assert
assertThrows(IllegalArgumentException.class,
() -> userService.findUserById(-1L));
// Verify repository was never called
verifyNoInteractions(mockRepository);
}
@Test
void shouldReturnTrueWhenUserIsActive() {
// Arrange
Long userId = 1L;
User activeUser = new User(userId, "Jane Doe", true);
when(mockRepository.findById(userId)).thenReturn(activeUser);
// Act
boolean isActive = userService.isUserActive(userId);
// Assert
assertTrue(isActive);
}
}
Advanced Mocking Techniques
Argument Matchers
Mockito provides flexible argument matchers for more dynamic stubbing:
import static org.mockito.ArgumentMatchers.*;
@Test
void demonstrateArgumentMatchers() {
UserRepository mockRepo = mock(UserRepository.class);
// Match any Long value
when(mockRepo.findById(any(Long.class)))
.thenReturn(new User(1L, "Default User", true));
// Match specific values
when(mockRepo.findById(eq(999L))).thenReturn(null);
// Match with custom conditions
when(mockRepo.findById(longThat(id -> id > 1000)))
.thenThrow(new RuntimeException("ID too large"));
// Test the behaviors
assertNotNull(mockRepo.findById(1L));
assertNull(mockRepo.findById(999L));
assertThrows(RuntimeException.class, () -> mockRepo.findById(1001L));
}
Verification with Times and Order
@Test
void demonstrateVerificationOptions() {
UserRepository mockRepo = mock(UserRepository.class);
UserService service = new UserService(mockRepo);
// Call methods multiple times
service.findUserById(1L);
service.findUserById(2L);
service.findUserById(1L);
// Verify exact number of calls
verify(mockRepo, times(2)).findById(1L);
verify(mockRepo, times(1)).findById(2L);
// Verify at least/most calls
verify(mockRepo, atLeast(1)).findById(1L);
verify(mockRepo, atMost(3)).findById(any(Long.class));
// Verify never called
verify(mockRepo, never()).findById(999L);
}
Using Annotations for Cleaner Code
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class UserServiceAnnotationTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testWithAnnotations() {
// No need for manual mock creation or injection
when(userRepository.findById(1L))
.thenReturn(new User(1L, "Test User", true));
User user = userService.findUserById(1L);
assertEquals("Test User", user.getName());
}
}
Real-World Examples and Use Cases
Mocking Complex Dependencies
Here's a more realistic example with multiple dependencies and external service calls:
public class OrderService {
private PaymentProcessor paymentProcessor;
private InventoryService inventoryService;
private EmailService emailService;
public OrderService(PaymentProcessor paymentProcessor,
InventoryService inventoryService,
EmailService emailService) {
this.paymentProcessor = paymentProcessor;
this.inventoryService = inventoryService;
this.emailService = emailService;
}
public OrderResult processOrder(Order order) {
// Check inventory
if (!inventoryService.isAvailable(order.getProductId(), order.getQuantity())) {
return OrderResult.failed("Insufficient inventory");
}
// Process payment
PaymentResult paymentResult = paymentProcessor.charge(
order.getCustomerId(), order.getTotalAmount());
if (!paymentResult.isSuccessful()) {
return OrderResult.failed("Payment failed: " + paymentResult.getErrorMessage());
}
// Update inventory
inventoryService.reserve(order.getProductId(), order.getQuantity());
// Send confirmation email
emailService.sendOrderConfirmation(order.getCustomerId(), order.getId());
return OrderResult.success(order.getId());
}
}
Testing this service with multiple mocks:
@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
@Mock PaymentProcessor paymentProcessor;
@Mock InventoryService inventoryService;
@Mock EmailService emailService;
@InjectMocks OrderService orderService;
@Test
void shouldProcessOrderSuccessfully() {
// Arrange
Order order = new Order("PROD-1", 2, "CUST-1", new BigDecimal("100.00"));
when(inventoryService.isAvailable("PROD-1", 2)).thenReturn(true);
when(paymentProcessor.charge("CUST-1", new BigDecimal("100.00")))
.thenReturn(PaymentResult.success("TXN-123"));
// Act
OrderResult result = orderService.processOrder(order);
// Assert
assertTrue(result.isSuccessful());
// Verify all dependencies were called in correct order
InOrder inOrder = inOrder(inventoryService, paymentProcessor, emailService);
inOrder.verify(inventoryService).isAvailable("PROD-1", 2);
inOrder.verify(paymentProcessor).charge("CUST-1", new BigDecimal("100.00"));
inOrder.verify(inventoryService).reserve("PROD-1", 2);
inOrder.verify(emailService).sendOrderConfirmation("CUST-1", order.getId());
}
@Test
void shouldFailWhenInsufficientInventory() {
// Arrange
Order order = new Order("PROD-1", 10, "CUST-1", new BigDecimal("500.00"));
when(inventoryService.isAvailable("PROD-1", 10)).thenReturn(false);
// Act
OrderResult result = orderService.processOrder(order);
// Assert
assertFalse(result.isSuccessful());
assertEquals("Insufficient inventory", result.getErrorMessage());
// Verify payment was never attempted
verifyNoInteractions(paymentProcessor);
verifyNoInteractions(emailService);
}
}
Testing Void Methods and Exceptions
@Test
void shouldHandleEmailServiceFailure() {
// Arrange
Order order = new Order("PROD-1", 1, "CUST-1", new BigDecimal("50.00"));
when(inventoryService.isAvailable(anyString(), anyInt())).thenReturn(true);
when(paymentProcessor.charge(anyString(), any(BigDecimal.class)))
.thenReturn(PaymentResult.success("TXN-456"));
// Make email service throw exception
doThrow(new EmailServiceException("SMTP server down"))
.when(emailService).sendOrderConfirmation(anyString(), anyString());
// Act & Assert
assertThrows(EmailServiceException.class, () -> orderService.processOrder(order));
// Verify inventory was still reserved despite email failure
verify(inventoryService).reserve("PROD-1", 1);
}
Framework Comparisons
Feature | Mockito | EasyMock | PowerMock | WireMock |
---|---|---|---|---|
Learning Curve | Easy | Moderate | Steep | Moderate |
Static Method Mocking | Yes (v3.4+) | No | Yes | N/A |
Final Class Mocking | Yes | No | Yes | N/A |
Annotation Support | Excellent | Good | Good | Limited |
HTTP Service Mocking | No | No | No | Excellent |
Community Support | Excellent | Good | Moderate | Good |
Performance | Fast | Fast | Slower | Fast |
Best Practices and Common Pitfalls
Do's
- Mock interfaces, not concrete classes – It's easier and more reliable
- Use @Mock annotations – Cleaner and more maintainable than manual mock creation
- Verify behavior, not just state – Check that methods were called with correct parameters
- Keep mocks simple – Don't over-complicate your mock setups
- Use argument matchers consistently – Mix specific values and matchers carefully
// Good: Simple, clear stubbing
when(userRepo.findById(1L)).thenReturn(testUser);
// Good: Using argument matchers appropriately
when(userRepo.findById(any(Long.class))).thenReturn(defaultUser);
// Bad: Mixing matchers and specific values incorrectly
// when(userRepo.findById(eq(1L), "someString")).thenReturn(testUser); // Won't work as expected
Common Pitfalls to Avoid
Over-Mocking
// Bad: Mocking everything including simple objects
@Mock
private String userName; // Don't mock simple value objects
@Mock
private List<String> userNames; // Don't mock standard collections
// Good: Mock only external dependencies
@Mock
private UserRepository userRepository;
@Mock
private ExternalApiClient apiClient;
Stubbing Without Verification
// Bad: Stubbing without verifying the interaction
@Test
void badTest() {
when(mockRepo.findById(1L)).thenReturn(user);
// Test code that might not even call findById
assertTrue(someCondition);
// No verification!
}
// Good: Always verify important interactions
@Test
void goodTest() {
when(mockRepo.findById(1L)).thenReturn(user);
User result = userService.getUser(1L);
assertEquals(user, result);
verify(mockRepo).findById(1L); // Verify the interaction happened
}
Ignoring Mock Reset
When using mocks across multiple tests, reset them to avoid test pollution:
public class UserServiceTest {
private UserRepository mockRepo = mock(UserRepository.class);
@BeforeEach
void setUp() {
reset(mockRepo); // Clear previous stubbing and interactions
// Or use @Mock annotations with MockitoExtension which handles this automatically
}
}
Performance Considerations and Benchmarks
Mockito's performance is generally excellent, but here are some numbers to keep in mind:
Operation | Time (nanoseconds) | Notes |
---|---|---|
Mock creation | ~50,000-100,000 | One-time cost per test |
Method stubbing | ~1,000-5,000 | Very fast |
Mock method call | ~100-500 | Minimal overhead |
Verification | ~1,000-3,000 | Depends on interaction history |
Performance Tips
- Reuse mocks when possible – But ensure proper reset between tests
- Avoid deep mock chains –
when(a.getB().getC().getD()).thenReturn(value)
is slow - Use lenient() for frequently called methods – Reduces overhead for unused stubbing warnings
@Test
void performanceOptimizedTest() {
UserRepository mockRepo = mock(UserRepository.class);
// Use lenient for methods called multiple times
lenient().when(mockRepo.findById(any(Long.class))).thenReturn(defaultUser);
lenient().when(mockRepo.existsById(any(Long.class))).thenReturn(true);
// Your test logic here
}
Integration with Testing Frameworks
Spring Boot Integration
Mockito works seamlessly with Spring Boot testing:
@SpringBootTest
public class UserControllerIntegrationTest {
@MockBean // Spring Boot annotation that creates and injects mock
private UserService userService;
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldReturnUserData() {
// Arrange
User mockUser = new User(1L, "John Doe", true);
when(userService.findById(1L)).thenReturn(mockUser);
// Act
ResponseEntity<User> response = restTemplate.getForEntity("/users/1", User.class);
// Assert
assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("John Doe", response.getBody().getName());
}
}
Testcontainers Integration
Sometimes you want to test against real databases but mock external services:
@Testcontainers
@SpringBootTest
public class OrderServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:13")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@MockBean
private PaymentService paymentService; // Mock external payment service
@Autowired
private OrderService orderService;
@Test
void shouldProcessOrderWithRealDatabase() {
// Real database, mocked payment service
when(paymentService.processPayment(any())).thenReturn(PaymentResult.success());
Order order = new Order("PROD-1", 1, "CUST-1", new BigDecimal("99.99"));
OrderResult result = orderService.processOrder(order);
assertTrue(result.isSuccessful());
}
}
Troubleshooting Common Issues
Mockito Cannot Mock Final Classes
If you're getting errors about final classes in older Mockito versions:
# Create file: src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
mock-maker-inline
Or use the newer dependency that includes this by default:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
UnfinishedStubbingException
This happens when you don't complete a stubbing call:
// Bad: Incomplete stubbing
when(mockRepo.findById(1L)); // Missing thenReturn/thenThrow
// Good: Complete stubbing
when(mockRepo.findById(1L)).thenReturn(user);
ArgumentMatchers Issues
// Bad: Mixing matchers and concrete values
when(service.processUser(eq(1L), "John")).thenReturn(result);
// Good: All matchers or all concrete values
when(service.processUser(eq(1L), eq("John"))).thenReturn(result);
// OR
when(service.processUser(1L, "John")).thenReturn(result);
Mockito has become the de-facto standard for Java mocking because it strikes the perfect balance between power and simplicity. The techniques covered here will handle 95% of your testing scenarios. For more advanced features like static method mocking, spy objects, and custom argument matchers, check out the official Mockito documentation.
Remember, good mocking is about testing behavior, not implementation details. Focus on verifying that your code calls the right dependencies with the right parameters, and you'll end up with tests that are both robust and maintainable.

This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.
This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.