Mockito in Java
Introduction
In professional software development, unit testing is critical. However, classes rarely exist in isolation; they depend on databases, web services, or other complex classes. Testing a class along with all its real dependencies becomes an Integration Test, which is slow and brittle.
Mockito is the most popular mocking framework for Java. It allows you to create "mock" objects—simulated objects that mimic the behavior of real dependencies. This isolates the class under test, ensuring your unit tests are fast, deterministic, and focused solely on the logic of that specific class.
The Core Concepts of Mocking
1. Creating Mocks
A mock object is an empty shell. By default, its methods do nothing and return default values (null, 0, or false).
You can create mocks programmatically or via annotations.
import static org.mockito.Mockito.*;
// Programmatic creation
List<String> mockList = mock(List.class);
2. Stubbing (Defining Behavior)
Stubbing is the process of telling the mock how to behave when specific methods are called.
// Tell the mock to return "Mockito" when get(0) is called
when(mockList.get(0)).thenReturn("Mockito");
// Tell the mock to throw an exception
when(mockList.get(1)).thenThrow(new IndexOutOfBoundsException());
System.out.println(mockList.get(0)); // Prints "Mockito"
3. Verification
Verification ensures that the class under test actually interacted with the mock exactly as expected.
mockList.add("Test");
mockList.clear();
// Verify that add() was called with "Test"
verify(mockList).add("Test");
// Verify that clear() was called exactly once
verify(mockList, times(1)).clear();
Annotation-Driven Mockito
In real-world applications (especially with frameworks like Spring), using annotations is cleaner and more manageable than creating mocks manually.
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 static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class) // Required for JUnit 5
class UserServiceTest {
// Creates a mock of the UserRepository
@Mock
private UserRepository userRepository;
// Creates a real instance of UserService and injects the mocked UserRepository into it
@InjectMocks
private UserService userService;
@Test
void testFindUser() {
// Arrange (Stubbing)
User fakeUser = new User("Alice");
when(userRepository.findById(1)).thenReturn(fakeUser);
// Act
User result = userService.getUser(1);
// Assert and Verify
assertEquals("Alice", result.getName());
verify(userRepository).findById(1); // Ensure the DB was actually "queried"
}
}
Advanced Features
1. Argument Matchers
Sometimes you don't care about the exact argument passed to a mock, just its type. You can use matchers like any(), anyInt(), or anyString().
when(userRepository.findByEmail(anyString())).thenReturn(fakeUser);
Note: If you use matchers for one argument, you must use matchers for all arguments in that method call.
2. Argument Captor
If you need to assert properties of an object that is created inside the method you are testing and passed to a mock, use an ArgumentCaptor.
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
userService.register("bob@example.com"); // Internally creates a User and calls save()
verify(userRepository).save(captor.capture());
User savedUser = captor.getValue();
assertEquals("bob@example.com", savedUser.getEmail());
3. Mocks vs. Spies
- Mock: A complete fake. Everything is stubbed out.
- Spy: A partial mock wrapping a real object. Real methods are executed unless explicitly stubbed.
List<String> realList = new ArrayList<>();
List<String> spyList = spy(realList);
spyList.add("Real Data"); // Actually adds to the list
when(spyList.size()).thenReturn(100); // Stubbed!
System.out.println(spyList.get(0)); // Prints "Real Data"
System.out.println(spyList.size()); // Prints 100
Summary
- Mockito isolates your code for unit testing by replacing dependencies with controlled fakes.
@Mockcreates fakes, and@InjectMocksinjects them into the class you are testing.- The standard testing flow is Arrange (Stubbing with
when()), Act (Calling your code), and Assert/Verify (Usingverify()). - Mastering Mockito is essential for Test-Driven Development (TDD) and ensuring robust Java applications.