Everything Java

← Back to blog

Published on Wed Jun 18 2025 13:20:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam

Mocking in Java with Mockito: A Complete Guide

Introduction

Unit testing is essential for writing reliable code, but testing classes that depend on other components can be challenging. Mocking allows you to create fake objects that simulate the behavior of real dependencies, making your tests faster, more reliable, and easier to write.

Mockito is the most popular mocking framework for Java, providing a clean and intuitive API for creating mocks, stubbing methods, and verifying interactions.

What is Mocking?

Mocking is a technique used in unit testing where you create fake objects (mocks) that simulate the behavior of real objects. This is useful when:

Adding Mockito to Your Project

Maven Dependencies

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>

Gradle Dependencies

testImplementation 'org.mockito:mockito-core:5.7.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.7.0'

Basic Mocking Concepts

Creating Mocks

Let’s start with a simple example. First, let’s create a service that depends on a repository:

public interface UserRepository {
User findById(Long id);
void save(User user);
List<User> findAll();
}
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User getUserById(Long id) {
return userRepository.findById(id);
}
public void createUser(User user) {
userRepository.save(user);
}
}
public class User {
private Long id;
private String name;
private String email;
// Constructors, getters, setters
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public Long getId() { return id; }
public String getName() { return name; }
public String getEmail() { return email; }
}

Now let’s write a test using Mockito:

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.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
private UserService userService;
@Test
void testGetUserById() {
// Arrange
User expectedUser = new User(1L, "John Doe", "john@example.com");
when(userRepository.findById(1L)).thenReturn(expectedUser);
userService = new UserService(userRepository);
// Act
User actualUser = userService.getUserById(1L);
// Assert
assertEquals(expectedUser, actualUser);
verify(userRepository).findById(1L);
}
}

Key Mockito Methods

  1. mock() - Creates a mock object
  2. when() - Stubs a method call
  3. thenReturn() - Specifies what the method should return
  4. verify() - Verifies that a method was called
  5. any() - Matches any argument

Stubbing Methods

Basic Stubbing

@Test
void testBasicStubbing() {
UserRepository mockRepo = mock(UserRepository.class);
// Stub a method to return a specific value
User user = new User(1L, "John", "john@example.com");
when(mockRepo.findById(1L)).thenReturn(user);
// Stub a method to return different values for different calls
when(mockRepo.findById(1L)).thenReturn(user);
when(mockRepo.findById(2L)).thenReturn(null);
// Stub a method to throw an exception
when(mockRepo.findById(999L)).thenThrow(new RuntimeException("User not found"));
}

Argument Matchers

Mockito provides argument matchers for flexible stubbing:

@Test
void testArgumentMatchers() {
UserRepository mockRepo = mock(UserRepository.class);
User user = new User(1L, "John", "john@example.com");
// Match any Long argument
when(mockRepo.findById(anyLong())).thenReturn(user);
// Match any argument of a specific type
when(mockRepo.save(any(User.class))).thenReturn(user);
// Match specific arguments
when(mockRepo.findById(eq(1L))).thenReturn(user);
// Match arguments that satisfy a condition
when(mockRepo.findById(argThat(id -> id > 0))).thenReturn(user);
}

Stubbing Void Methods

@Test
void testStubbingVoidMethods() {
UserRepository mockRepo = mock(UserRepository.class);
User user = new User(1L, "John", "john@example.com");
// Stub a void method to do nothing (default behavior)
doNothing().when(mockRepo).save(user);
// Stub a void method to throw an exception
doThrow(new RuntimeException("Database error")).when(mockRepo).save(user);
}

Verifying Interactions

Basic Verification

@Test
void testVerification() {
UserRepository mockRepo = mock(UserRepository.class);
UserService userService = new UserService(mockRepo);
User user = new User(1L, "John", "john@example.com");
// Stub the method
when(mockRepo.findById(1L)).thenReturn(user);
// Call the method
userService.getUserById(1L);
// Verify the method was called
verify(mockRepo).findById(1L);
// Verify the method was called exactly once
verify(mockRepo, times(1)).findById(1L);
// Verify the method was never called
verify(mockRepo, never()).save(any(User.class));
}

Advanced Verification

@Test
void testAdvancedVerification() {
UserRepository mockRepo = mock(UserRepository.class);
UserService userService = new UserService(mockRepo);
User user = new User(1L, "John", "john@example.com");
// Stub methods
when(mockRepo.findById(1L)).thenReturn(user);
when(mockRepo.save(any(User.class))).thenReturn(user);
// Call methods
userService.getUserById(1L);
userService.createUser(user);
// Verify method was called with specific arguments
verify(mockRepo).findById(eq(1L));
verify(mockRepo).save(argThat(u -> u.getName().equals("John")));
// Verify method was called at least once
verify(mockRepo, atLeastOnce()).findById(anyLong());
// Verify method was called at most twice
verify(mockRepo, atMost(2)).findById(anyLong());
// Verify no more interactions
verifyNoMoreInteractions(mockRepo);
}

Spying

Spies are partial mocks that use real objects but allow you to stub specific methods:

@Test
void testSpying() {
List<String> list = new ArrayList<>();
List<String> spyList = spy(list);
// Stub a method on the spy
when(spyList.size()).thenReturn(100);
// Use real methods
spyList.add("one");
spyList.add("two");
// Stubbed method returns fake value
assertEquals(100, spyList.size());
// Real method returns actual value
assertEquals(2, spyList.size());
// Verify interactions
verify(spyList).add("one");
verify(spyList).add("two");
}

Mocking Static Methods

Mockito 3.4+ supports mocking static methods:

@Test
void testStaticMethodMocking() {
// Mock static method
try (MockedStatic<UtilityClass> mockedStatic = mockStatic(UtilityClass.class)) {
mockedStatic.when(UtilityClass::getCurrentTime).thenReturn("2023-01-01");
// Use the mocked static method
String time = UtilityClass.getCurrentTime();
assertEquals("2023-01-01", time);
// Verify the static method was called
mockedStatic.verify(UtilityClass::getCurrentTime);
}
}
class UtilityClass {
public static String getCurrentTime() {
return LocalDateTime.now().toString();
}
}

Integration with JUnit 5

Using @ExtendWith

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService; // Automatically injects mocks
@Test
void testWithInjectedMocks() {
User user = new User(1L, "John", "john@example.com");
when(userRepository.findById(1L)).thenReturn(user);
User result = userService.getUserById(1L);
assertEquals(user, result);
verify(userRepository).findById(1L);
}
}

Using @Mock and @InjectMocks

@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private OrderRepository orderRepository;
@InjectMocks
private OrderService orderService;
@Test
void testCreateOrder() {
User user = new User(1L, "John", "john@example.com");
Order order = new Order(1L, user, 100.0);
when(userRepository.findById(1L)).thenReturn(user);
when(orderRepository.save(any(Order.class))).thenReturn(order);
Order result = orderService.createOrder(1L, 100.0);
assertNotNull(result);
verify(userRepository).findById(1L);
verify(orderRepository).save(any(Order.class));
}
}

Best Practices

  1. Use Descriptive Test Names: Make your test method names clear and descriptive.

  2. Follow AAA Pattern: Arrange, Act, Assert - structure your tests clearly.

  3. Mock at Boundaries: Mock external dependencies, not your own code.

  4. Don’t Over-Mock: Only mock what you need to test the specific behavior.

  5. Use Argument Matchers Sparingly: Prefer exact arguments when possible.

  6. Verify Important Interactions: Don’t verify every method call, just the important ones.

  7. Keep Tests Simple: Each test should focus on one specific behavior.

Common Pitfalls

  1. Mocking Everything: Don’t mock objects that are simple and don’t have external dependencies.

  2. Over-Verification: Don’t verify every method call; focus on the important interactions.

  3. Complex Stubbing: Keep your stubbing simple and readable.

  4. Not Cleaning Up: Remember to close MockedStatic resources.

  5. Testing Implementation Details: Focus on testing behavior, not implementation.

Advanced Features

Capturing Arguments

@Test
void testCapturingArguments() {
UserRepository mockRepo = mock(UserRepository.class);
UserService userService = new UserService(mockRepo);
User user = new User(1L, "John", "john@example.com");
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
userService.createUser(user);
verify(mockRepo).save(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals("John", capturedUser.getName());
assertEquals("john@example.com", capturedUser.getEmail());
}

Mocking Final Classes and Methods

@Test
void testMockingFinalClass() {
// Mockito 2.x+ supports mocking final classes
FinalClass finalClass = mock(FinalClass.class);
when(finalClass.finalMethod()).thenReturn("mocked");
assertEquals("mocked", finalClass.finalMethod());
}
final class FinalClass {
public final String finalMethod() {
return "real";
}
}

Conclusion

Mockito is a powerful and flexible mocking framework that makes unit testing much easier. By understanding the basic concepts of mocking, stubbing, and verification, you can write more effective and maintainable tests.

Start with simple mocks and gradually explore more advanced features as your testing needs grow. Remember that the goal is to test your code in isolation while ensuring it behaves correctly with its dependencies.

Resources

Written by Purusothaman Ramanujam

← Back to blog