Mockito — Mocking, Stubs, Verify, BDDMockito, ArgumentCaptor, and Spy
In this tutorial, you will learn about Mockito. We cover key concepts, practical examples, and best practices to help you master this topic.
Mockito is the most popular Java mocking framework, enabling creation of mock objects for isolating code under test. Mocks simulate the behavior of real dependencies — a mock Repository returns test data without accessing a database, and a mock HTTP client returns a canned response without making network calls.
What You'll Learn
- Creating mocks with @Mock and Mockito.mock()
- Stubbing with when/thenReturn and doThrow/when
- Verifying interactions with verify
- BDDMockito for behavior-driven development style
- ArgumentCaptor for capturing method arguments
- @Spy for partial mocking
Why It Matters
Unit tests should test one unit of code in isolation. Without mocking, a test for a service class becomes an integration test that depends on databases, APIs, and file systems — making tests slow, flaky, and hard to maintain.
Real-World Use
Mockito is used in virtually every Java project with automated tests. Spring Boot includes spring-boot-starter-test with Mockito pre-configured.
Setting Up Mockito
// Maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
Creating Mocks
Using Annotations
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldFindUser() {
when(userRepository.findById(1L)).thenReturn(Optional.of(new User("Alice")));
User user = userService.getUser(1L);
assertEquals("Alice", user.getName());
}
}
@InjectMocks injects the @Mock fields into the UserService constructor (or setters/fields).
Without Annotations
UserRepository mockRepo = Mockito.mock(UserRepository.class);
UserService service = new UserService(mockRepo);
Stubbing
Standard Stubbing
// Return a value
when(repository.findById(1L)).thenReturn(Optional.of(user));
// Throw an exception
when(repository.save(any())).thenThrow(new DataIntegrityViolationException("Duplicate"));
// Return different values on successive calls
when(repository.findById(anyLong()))
.thenReturn(Optional.of(user)) // first call
.thenThrow(new RuntimeException()); // second call
Void Methods
doNothing().when(repository).delete(anyLong());
doThrow(new RuntimeException("Delete failed")).when(repository).delete(1L);
Argument Matchers
when(repository.findById(1L)).thenReturn(Optional.of(user));
when(repository.findById(anyLong())).thenReturn(Optional.empty());
when(repository.findByName(anyString())).thenReturn(List.of(user));
when(repository.save(argThat(u -> u.getAge() > 18))).thenReturn(user);
Available matchers: any(), anyInt(), anyString(), anyList(), eq(value), argThat(predicate).
Verification
Verify that specific interactions occurred:
@Test
void shouldSaveUser() {
userService.createUser("Bob");
verify(repository).save(any(User.class));
}
@Test
void shouldCallDeleteExactlyOnce() {
userService.deleteUser(1L);
verify(repository, times(1)).delete(1L);
}
// Never called
verify(repository, never()).delete(anyLong());
// Called at least once
verify(repository, atLeastOnce()).save(any());
// No more interactions
verifyNoMoreInteractions(repository);
// Order verification
InOrder inOrder = inOrder(repository, emailService);
inOrder.verify(repository).save(any());
inOrder.verify(emailService).sendWelcome(any());
BDDMockito
Behavior-Driven Development style uses given/willReturn/then:
import static org.mockito.BDDMockito.*;
@Test
void shouldReturnUser() {
// Given
given(userRepository.findById(1L)).willReturn(Optional.of(user));
// When
User result = userService.getUser(1L);
// Then
then(userRepository).should().findById(1L);
assertEquals("Alice", result.getName());
}
BDDMockito aligns with the Given/When/Then structure of BDD tests.
ArgumentCaptor
Capture arguments passed to mock methods for further assertions:
@Test
void shouldSendEmailWithCorrectContent() {
// When
userService.registerUser("Alice", "alice@example.com");
// Then
ArgumentCaptor<Email> captor = ArgumentCaptor.forClass(Email.class);
verify(emailService).send(captor.capture());
Email captured = captor.getValue();
assertEquals("alice@example.com", captured.getTo());
assertEquals("Welcome!", captured.getSubject());
}
Spy — Partial Mocking
A spy wraps a real object, allowing you to stub specific methods while keeping real behavior for others:
@Test
void shouldUseSpy() {
List<String> list = new ArrayList<>();
List<String> spy = Mockito.spy(list);
// Stub size() to return 100
doReturn(100).when(spy).size();
// Real behavior for other methods
spy.add("A");
spy.add("B");
assertEquals(100, spy.size()); // stubbed
assertEquals("A", spy.get(0)); // real
}
Common Mistakes
- Not using
@ExtendWith(MockitoExtension.class). Without this,@Mockand@InjectMocksare not processed. - Stubbing void methods with
when(). UsedoThrow()/doNothing()for void methods. - Mixing matchers and concrete values incorrectly. If you use matchers for one argument, all arguments must use matchers:
// WRONG when(repository.save(any(), "fixed")); // RIGHT when(repository.save(any(), eq("fixed")));
- Over-mocking. If a test requires 5+ mocks, consider if the class under test has too many dependencies.
- Verifying interactions that do not matter. Only verify interactions that are essential to the test's scenario.
Practice Questions
1. What is the difference between @Mock and @InjectMocks?
@Mock creates a mock instance. @InjectMocks creates a real instance and injects the mocks into it (via constructor, setter, or field).
2. What does verify(mock, times(2)).method() do?
It verifies that method was called exactly twice on the mock. Fails if called more or fewer times.
3. How do you capture a method argument in Mockito?
Use ArgumentCaptor. Create a captor for the type, call verify(mock).method(captor.capture()), then inspect captor.getValue().
4. What is the difference between a mock and a spy?
A mock has no real behavior — all methods return defaults (0, null, false). A spy wraps a real object; unstubbed methods execute real code.
5. How do you stub a void method to throw an exception?
doThrow(new RuntimeException()).when(mock).method().
Challenge Question:
Write a test for an OrderService that depends on InventoryService, PaymentService, and NotificationService. Use Mockito to stub InventoryService.checkStock() returning true, PaymentService.charge() returning a PaymentResult, and verify that NotificationService.sendConfirmation() is called once. Use ArgumentCaptor to verify the notification content.
FAQ
Mini Project
Write comprehensive Mockito tests for a LibraryService:
LibraryServicedepends onBookRepository,MemberRepository, andEmailService- Test
borrowBook: stub repository methods, verify interactions - Test
borrowBookwhen book is not available — stub to return Optional.empty() - Test
returnBook: verify email is sent with correct content usingArgumentCaptor - Test
getOverdueBooks: stub a repository method that throws an exception - Use BDDMockito style (given/willReturn/then/should)
- Use
@InjectMocksand@Mockannotations - Add a spy test for a partial mock scenario
What's Next
Unit tests validate individual components. Integration tests validate the system as a whole. Lesson 49 covers integration testing — Testcontainers for database testing, @SpringBootTest for application context loading, and embedded databases for fast in-memory testing.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro