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:
- The real object is difficult to set up (e.g., database connections)
- The real object has non-deterministic behavior (e.g., current time)
- The real object is slow or has side effects
- You want to test your code in isolation
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
mock()- Creates a mock objectwhen()- Stubs a method callthenReturn()- Specifies what the method should returnverify()- Verifies that a method was calledany()- Matches any argument
Stubbing Methods
Basic Stubbing
@Testvoid 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:
@Testvoid 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
@Testvoid 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
@Testvoid 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
@Testvoid 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:
@Testvoid 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:
@Testvoid 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
-
Use Descriptive Test Names: Make your test method names clear and descriptive.
-
Follow AAA Pattern: Arrange, Act, Assert - structure your tests clearly.
-
Mock at Boundaries: Mock external dependencies, not your own code.
-
Don’t Over-Mock: Only mock what you need to test the specific behavior.
-
Use Argument Matchers Sparingly: Prefer exact arguments when possible.
-
Verify Important Interactions: Don’t verify every method call, just the important ones.
-
Keep Tests Simple: Each test should focus on one specific behavior.
Common Pitfalls
-
Mocking Everything: Don’t mock objects that are simple and don’t have external dependencies.
-
Over-Verification: Don’t verify every method call; focus on the important interactions.
-
Complex Stubbing: Keep your stubbing simple and readable.
-
Not Cleaning Up: Remember to close MockedStatic resources.
-
Testing Implementation Details: Focus on testing behavior, not implementation.
Advanced Features
Capturing Arguments
@Testvoid 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
@Testvoid 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