What Mockito is for, creating mocks, @Mock and @InjectMocks, when/thenReturn, mock vs spy, void methods, doReturn/doThrow/doAnswer, verifying interactions, simulating exceptions, and ArgumentCaptor with a real example.
Published September 25, 2026
Mockito questions are best answered with short, correct snippets. Interviewers also listen for judgement: when not to mock, and why stubbing with doReturn matters for spies. All the examples assume Mockito 5 with JUnit 5 (@ExtendWith(MockitoExtension.class)).
Short answer: Mockito is a mocking framework. It creates test doubles for a class's collaborators, so you can test the class in isolation. You stub what the collaborators return (when(...).thenReturn(...)), and verify how they were called (verify(...)), without real databases, HTTP calls or email servers. Tests become fast and deterministic, and you can force error paths that are hard to reproduce for real.
Key points to cover:
Short answer: Use Mockito.mock(Type.class), or declare @Mock fields with MockitoExtension. An unstubbed mock returns defaults: null, 0, false, empty collections and Optional.empty().
@ExtendWith(MockitoExtension.class)
class PaymentServiceTest {
@Mock PaymentGateway gateway; // equivalent to mock(PaymentGateway.class)
@Test void approvesPayment() {
when(gateway.charge(any(ChargeRequest.class))).thenReturn(ChargeResult.approved("ch_1"));
var service = new PaymentService(gateway);
assertTrue(service.pay(new Order(1L, new BigDecimal("499.00"))).approved());
}
}
@Mock and @InjectMocks for?Short answer: @Mock creates a mock field. @InjectMocks creates the object under test, and injects the @Mock (and @Spy) fields into it: through the biggest constructor first, then setters, then fields.
Key points to cover:
@InjectMocks fails silently. If it can't match a dependency, the field stays null, and you get an NPE later. With constructor injection, many teams instantiate the class explicitly in @BeforeEach (new CheckoutService(gateway, orders)), which is clearer, and fails at compile time when the dependencies change.MockitoExtension uses strict stubs. Unused stubbings fail the test (UnnecessaryStubbingException), which keeps tests honest.when and thenReturn?Short answer: when(mock.method(args)).thenReturn(value) stubs a call. You can chain several values for consecutive calls, match arguments with matchers, or compute the answer with thenAnswer.
when(repo.findById(42L)).thenReturn(Optional.of(order));
when(rates.current("USD")).thenReturn(83.1, 83.4); // first call 83.1, then 83.4
when(repo.findByStatus(eq(OrderStatus.PAID), any(Pageable.class))).thenReturn(Page.empty());
when(idGen.next()).thenAnswer(inv -> UUID.randomUUID().toString());
Common trap: mixing a raw value with matchers, as in when(svc.find(42L, any())). Once one argument uses a matcher, all arguments must. Write eq(42L), any().
mock() and spy()?Short answer: A mock is a complete fake: every method does nothing, or returns a default, unless stubbed. A spy wraps a real object: methods run the real code unless you stub them. Spies are useful to partially override legacy objects, or to verify calls on a real implementation.
List<String> real = new ArrayList<>();
List<String> spy = spy(real);
spy.add("a"); // real behaviour: really added
doReturn(100).when(spy).size(); // stub one method
Key points to cover:
doReturn(...).when(spy) with spies. when(spy.size()) calls the real method while stubbing, which can have side effects, or throw.void?Short answer: Mocks already do nothing for void methods, so usually no stubbing is needed. Just verify the call. To change the behaviour, use the do…() family:
doThrow(new MailException("SMTP down")).when(mailer).send(any(Email.class)); // simulate a failure
doNothing().when(spyNotifier).notify(any()); // silence a real method on a spy
doAnswer(inv -> { sent.add(inv.getArgument(0)); return null; }).when(mailer).send(any()); // capture side effects
doReturn(), doThrow() and doAnswer() used for?Short answer: The do…().when(mock).method() form is needed when when(mock.method()) can't be used:
when() needs a return value to wrap.Each one:
doReturn returns a value.doThrow throws an exception, to test error handling.doAnswer runs custom logic: it computes the result from the arguments, mutates an argument, or invokes a callback.doAnswer(inv -> {
Consumer<Result> callback = inv.getArgument(1);
callback.accept(Result.ok()); // simulate an async client calling back
return null;
}).when(asyncClient).fetch(eq("orders"), any());
Short answer: Use verify(mock).method(args), optionally with a count (times(n), never(), atLeastOnce(), atMost(n)), argument matchers, ordering (inOrder), or timeouts for async code (timeout(500)).
verify(mailer).send(argThat(e -> e.to().equals("asha@example.com")));
verify(orders, never()).save(any());
InOrder order = inOrder(inventory, payments);
order.verify(inventory).reserve(any());
order.verify(payments).charge(any());
verify(eventPublisher, timeout(1000)).publish(any(OrderPlaced.class));
Key points to cover:
Short answer: For non-void methods, use when(mock.method()).thenThrow(new X(...)), or thenThrow(X.class). For void methods, use doThrow(...).when(mock).method(). Then assert how your code handles the error.
when(inventory.reserve(any())).thenThrow(new InventoryUnavailableException("timeout"));
var ex = assertThrows(OrderFailedException.class, () -> checkout.place(cart));
verify(payments, never()).charge(any()); // compensation logic: no charge when reservation fails
Key points to cover:
MockitoException: Checked exception is invalid for this method).ArgumentCaptor work? Give an example.Short answer: ArgumentCaptor captures the arguments passed to a mock, so you can make detailed assertions about objects your code builds internally, for example the entity passed to save(), or the message passed to send().
@Captor ArgumentCaptor<User> userCaptor;
@Test
void registersUserWithNormalisedEmailAndHashedPassword() {
registration.register(new SignupRequest(" Asha@Example.COM ", "s3cret!"));
verify(userRepository).save(userCaptor.capture());
User saved = userCaptor.getValue();
assertEquals("asha@example.com", saved.getEmail());
assertNotEquals("s3cret!", saved.getPasswordHash()); // never stored in plaintext
assertTrue(passwordEncoder.matches("s3cret!", saved.getPasswordHash()));
}
Key points to cover:
getAllValues() returns every capture, when there were several calls.argThat(...) for simple checks, and captors when you need several assertions on the argument.Q: What are strict stubs, and why does MockitoExtension use them?
A: Stubbings that are never used cause the test to fail, and argument mismatches are reported clearly. That keeps tests free of dead setup, and catches wrong arguments early. Use lenient() for the rare legitimate exception.
Q: What does @MockitoBean do differently from @Mock?
A: @MockitoBean (Spring Framework 6.2+) replaces a bean inside the Spring application context with a mock, for slice and integration tests. @Mock is a plain Mockito mock, used in unit tests without Spring.
Q: How do you reset a mock?
A: reset(mock) exists, but it's a smell: each test should create fresh mocks (the extension does this per test). Needing a reset usually means the test does too much.
Q: Can Mockito mock equals/hashCode?
A: No. Mockito doesn't stub equals() or hashCode(), because it relies on them internally. Use real value objects instead.