Contents
- Dependency & Setup
- Creating Mocks
- Stubbing — when/thenReturn
- Stubbing Exceptions & Void Methods
- Spies — Partial Mocking
- Verifying Interactions
- ArgumentCaptor
- Argument Matchers
Spring Boot's spring-boot-starter-test includes Mockito. Standalone projects add mockito-junit-jupiter for JUnit 5 integration.
<!-- Spring Boot — Mockito already included -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Standalone -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>
Enable Mockito annotations in JUnit 5 tests by adding @ExtendWith(MockitoExtension.class) to the test class.
Create mocks with the @Mock annotation (preferred) or the programmatic Mockito.mock() call. Inject mocks into the class under test using @InjectMocks.
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.*;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.Assertions;
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
OrderRepository orderRepo; // Mockito creates a mock implementation
@Mock
EmailService emailService;
@InjectMocks
OrderService orderService; // Mockito injects the mocks above via constructor/setter/field
@Test
void placesOrder() {
Order saved = new Order("o1", "Widget", 2);
Mockito.when(orderRepo.save(Mockito.any())).thenReturn(saved);
Order result = orderService.place("Widget", 2);
Assertions.assertEquals("o1", result.id());
}
}
@InjectMocks tries constructor injection first, then setter injection, then field injection. Prefer constructor injection in your production classes — it makes the dependencies explicit and works reliably with @InjectMocks.
Use when(mock.method(args)).thenReturn(value) to define what a mock returns for a given call. Chain multiple thenReturn calls to return different values on successive invocations.
@Test
void stubbingExamples() {
UserRepository repo = Mockito.mock(UserRepository.class);
// Always return the same value
Mockito.when(repo.findById(1L))
.thenReturn(Optional.of(new User(1L, "Alice")));
// Return different values on successive calls
Mockito.when(repo.count())
.thenReturn(0L) // first call
.thenReturn(1L) // second call
.thenReturn(2L); // third and subsequent calls
// Compute a value dynamically with thenAnswer
Mockito.when(repo.findById(Mockito.anyLong()))
.thenAnswer(inv -> {
long id = inv.getArgument(0);
return Optional.of(new User(id, "User-" + id));
});
Assertions.assertEquals("User-42", repo.findById(42L).get().name());
}
Use thenThrow to make a mock throw an exception. For void methods use the doThrow(...).when(mock).method() syntax.
@Test
void stubbingExceptions() {
PaymentGateway gateway = Mockito.mock(PaymentGateway.class);
// Throw on a non-void method
Mockito.when(gateway.charge(Mockito.anyDouble()))
.thenThrow(new PaymentException("Declined"));
Assertions.assertThrows(PaymentException.class, () -> gateway.charge(50.0));
}
@Test
void stubbingVoidMethods() {
NotificationService notifier = Mockito.mock(NotificationService.class);
// void methods need doThrow/doNothing/doAnswer
Mockito.doThrow(new RuntimeException("SMTP down"))
.when(notifier).sendEmail(Mockito.anyString());
Mockito.doNothing()
.when(notifier).sendSms(Mockito.anyString()); // explicit no-op (default for void)
Assertions.assertThrows(RuntimeException.class, () -> notifier.sendEmail("a@b.com"));
}
A @Spy wraps a real object. Unstubbed methods execute normally; stubbed methods return the configured value. Use spies sparingly — they often indicate a design that could be improved.
@ExtendWith(MockitoExtension.class)
class PricingServiceTest {
@Spy
PricingService pricingService = new PricingService(new TaxConfig(0.2));
@Test
void spyCallsRealMethodByDefault() {
// Calls the real calculateBase() implementation
double base = pricingService.calculateBase("Widget", 3);
Assertions.assertTrue(base > 0);
}
@Test
void spyCanStubSpecificMethod() {
// Override just one method, rest are real
Mockito.doReturn(100.0).when(pricingService).calculateBase(Mockito.anyString(), Mockito.anyInt());
double total = pricingService.calculateTotal("Widget", 3); // uses stubbed base
Assertions.assertEquals(120.0, total); // 100 * 1.2 tax
}
}
Always use doReturn(...).when(spy).method() syntax when stubbing spy methods — never when(spy.method()).thenReturn(...). The latter actually calls the real method during the stub setup, which can cause side effects or NPEs.
Use verify() to assert that a mock method was called with expected arguments, the right number of times, or in the right order.
@Test
void verificationExamples() {
UserRepository repo = Mockito.mock(UserRepository.class);
AuditLogger audit = Mockito.mock(AuditLogger.class);
UserService svc = new UserService(repo, audit);
svc.deleteUser(42L);
// Was called exactly once with this argument
Mockito.verify(repo, Mockito.times(1)).deleteById(42L);
// Was called at least once
Mockito.verify(audit, Mockito.atLeastOnce()).log(Mockito.anyString());
// Was never called
Mockito.verify(repo, Mockito.never()).save(Mockito.any());
// No other interactions happened on this mock
Mockito.verifyNoMoreInteractions(repo);
}
@Test
void verifyOrder() {
DataSource ds = Mockito.mock(DataSource.class);
Notifier notifier = Mockito.mock(Notifier.class);
BatchJob job = new BatchJob(ds, notifier);
job.run();
InOrder inOrder = Mockito.inOrder(ds, notifier);
inOrder.verify(ds).connect();
inOrder.verify(ds).process();
inOrder.verify(notifier).send("done");
}
ArgumentCaptor captures the argument passed to a mock method so you can assert on it. Use it when you need to verify the exact object that was passed, not just whether the method was called.
@ExtendWith(MockitoExtension.class)
class OrderServiceCaptorTest {
@Mock OrderRepository repo;
@Captor ArgumentCaptor<Order> orderCaptor;
@InjectMocks OrderService orderService;
@Test
void capturesOrderOnSave() {
orderService.place("Gadget", 5);
Mockito.verify(repo).save(orderCaptor.capture());
Order saved = orderCaptor.getValue();
Assertions.assertEquals("Gadget", saved.product());
Assertions.assertEquals(5, saved.quantity());
Assertions.assertNotNull(saved.createdAt());
}
}
Argument matchers let stubs and verifications match arguments flexibly. When using matchers, all arguments in the same method call must be matchers — mix matchers and literal values using eq().
UserRepository repo = Mockito.mock(UserRepository.class);
// Common matchers
Mockito.when(repo.findByEmail(Mockito.anyString()))
.thenReturn(Optional.empty());
Mockito.when(repo.findByAgeGreaterThan(Mockito.intThat(age -> age >= 18)))
.thenReturn(List.of(new User(1L, "Alice")));
// Mixing literal and matcher — use eq() for the literal
Mockito.when(repo.findByNameAndCity(Mockito.eq("Alice"), Mockito.anyString()))
.thenReturn(Optional.of(new User(1L, "Alice")));
// Custom matcher with argThat
Mockito.verify(repo).save(Mockito.argThat(
order -> order.quantity() > 0 && order.product() != null
));
| Matcher | Matches |
| any() / any(Class) | Anything, including null |
| anyString(), anyInt(), … | Any non-null value of that type |
| eq(value) | Equals the given value |
| isNull() / isNotNull() | Null / non-null |
| startsWith(str), contains(str) | String prefix / substring |
| argThat(predicate) | Custom predicate |
| intThat(predicate), longThat()… | Custom predicate for primitives |