Consecutive Call Stubbing
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
If you need a mocked method to return different values on consecutive calls, Mockito makes that really easy.
You just pass multiple values to then-return, like then-return-val-one-comma-val-two-comma-val-three. The first time the method is called, it returns val-one. The second call returns val-two, the third returns val-three.
What happens after that is the last value keeps repeating forever. So if you call it a fourth and fifth time, you'll still get val-three. This is super useful for testing retry logic.
Let's say you're testing a payment service that retries failed transactions. You'd stub it with then-return-failed-then-return-success. The first attempt returns failed, triggering a retry, and the second attempt returns success.
Your test verifies the service tried twice and ultimately succeeded. Another common use case is pagination. You stub the fetch method to return page one on the first call, page two on the second call, and an empty list on the third call to signal you've reached the end.
One mistake people make is calling when-mock-method-then-return multiple times on separate lines, thinking that creates a sequence. It doesn't — the second when call just overwrites the first one. You have to chain the then-returns or use the comma syntax in a single then-return call.
Consecutive Call Stubbing
Direct Answer
Use when(mock.method()).thenReturn(val1, val2, val3) or chain multiple thenReturn calls. The first call returns val1, second returns val2, third returns val3. After the last value, subsequent calls return the last value repeatedly.
Code Example
@ExtendWith(MockitoExtension.class)
class PaymentServiceTest {
@Mock
private PaymentGateway paymentGateway;
@InjectMocks
private PaymentService paymentService;
@Test
void testRetryOnFailure() {
// First call fails, second succeeds
when(paymentGateway.processPayment(anyString()))
.thenReturn("FAILED")
.thenReturn("SUCCESS");
String result = paymentService.processWithRetry("txn-123");
assertThat(result).isEqualTo("SUCCESS");
verify(paymentGateway, times(2)).processPayment("txn-123");
}
@Test
void testPaginatedFetch() {
// Simulate three pages of results
when(paymentGateway.fetchTransactions(anyInt()))
.thenReturn(
List.of("txn-1", "txn-2"),
List.of("txn-3", "txn-4"),
List.of() // Empty list signals end
);
var allTransactions = paymentService.fetchAllTransactions();
assertThat(allTransactions).hasSize(4);
verify(paymentGateway, times(3)).fetchTransactions(anyInt());
}
}
Common Mistakes & Pitfalls
- Multiple when() calls overwriting each other: Freshers often write when(mock.method()).thenReturn(val1) on one line, then when(mock.method()).thenReturn(val2) on the next, thinking both will apply. Only the last stubbing survives — the second when() overwrites the first completely. You must chain thenReturn calls or pass multiple values to a single thenReturn.
- Not understanding the repeat behavior: After the sequence is exhausted, the last value repeats forever. If you stub with thenReturn(1, 2) and call the method five times, you get 1, 2, 2, 2, 2. Freshers sometimes expect an exception or null after the sequence ends.
- Mixing with argument matchers: If you stub with consecutive values but use different argument matchers in later when() calls, you're creating separate stubbings, not extending the sequence. Keep the argument matchers identical when chaining.
Check your understanding
Answer these without looking back at the article.
- How would you test a retry mechanism using consecutive stubbing?
- What happens if you call a method more times than you have stubbed?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.