A mailer collaborator's send(...) method has return type void. Using Mockito, how do you stub it so the first invocation throws an IOException and every later invocation succeeds silently?
answer
- void has no value → do-family only
- doThrow(...).doNothing().when(mock).call()
- tail entry repeats → put recovery last
- checked exception must be declared
- doNothing() illegal on value-returning methods
basics
~10 sUse the do-family with a chain: doThrow(new IOException()).doNothing().when(mailer).send(any()). First call throws, later calls do nothing, because the last entry in the chain repeats. IOException must be declared by send(...) or Mockito rejects the stub.
solid answer
~50 sVoid methods cannot be wrapped in `when(...)`, so the sequence is built on a `Stubber`: ```java doThrow(new IOException("smtp down")) .doNothing() .when(mailer).send(any(Message.class)); ``` The first `send(...)` throws; the second and every subsequent call do nothing, because the final entry in the chain is the sticky answer. Order matters — put the failure first and the recovery last, since reversing the chain would make the mock throw forever. Two constraints worth naming. `IOException` is checked, so `send(...)` must declare it; otherwise Mockito rejects the stubbing on the spot with "Checked exception is invalid for this method". And `doNothing()` is only legal for void methods — using it on a value-returning method raises a `MockitoException`. On a plain mock, `doNothing()` alone is redundant (void methods already do nothing by default); its real value is as a chain link like this, and on spies where it suppresses the real body. Finish by verifying the retry actually happened: `verify(mailer, times(2)).send(any())`.
code
java · 11 lines@Test
void resendsAfterTransientSmtpFailure() throws Exception {
doThrow(new IOException("smtp down"))
.doNothing()
.when(mailer).send(any(Message.class));
boolean delivered = dispatcher.dispatch(message);
assertTrue(delivered);
verify(mailer, times(2)).send(any(Message.class));
}go deeper
Show the doThrow(...).doNothing().when(mock).send(...) shape and say why when(...) cannot wrap a void call.
Explain the sticky-tail rule that makes ordering meaningful, and name the checked-exception-must-be-declared validation.
Insist on pairing the stub with verify(times(n)) since the repeating tail hides extra calls, and mention ArgumentCaptor to prove the retry resent the same payload.
Discuss what the test is really pinning down — the retry policy contract — and whether the failure mode belongs in a unit test with a mock or in an integration test against a fake SMTP endpoint.
## Why void needs a different shape Mockito's familiar stubbing form, `when(mock.call()).thenReturn(x)`, is an ordinary Java method call: the argument to `when(...)` is the *value* of `mock.call()`. A method declared `void` produces no value, so `when(mailer.send(msg))` is not even legal Java. Mockito therefore offers a mirrored syntax where the behaviour comes first and the call being stubbed comes last: ```java doThrow(new IOException()).when(mailer).send(msg); ``` Read it as "do this ... when this call happens". Mechanically, `when(mailer)` puts that mock into stubbing mode and returns it; the very next invocation on it is *recorded* as the stubbed call rather than executed. ## Building the sequence `doThrow(...)` returns a `Stubber`, and `Stubber` exposes `doThrow`, `doNothing`, `doAnswer`, `doReturn` and `doCallRealMethod` again — so behaviours chain exactly like `thenReturn` chains: ```java doThrow(new IOException("smtp down")) .doNothing() .when(mailer).send(any(Message.class)); ``` Invocation 1 throws. Invocation 2 does nothing. Invocation 3, 4, 5 also do nothing: the last entry in a consecutive chain is repeated forever once earlier entries are consumed. That persistence rule is what makes "fail once, then always succeed" expressible in two links rather than one per expected call. To model a stubborn outage — fails twice then recovers — just add a link: ```java doThrow(new IOException()).doThrow(new IOException()).doNothing() .when(mailer).send(any(Message.class)); ``` ## Ordering is the whole trick Because the tail is sticky, the *order* of the chain encodes the story. `doThrow(...).doNothing()` means transient failure. `doNothing().doThrow(...)` means "works once, then breaks permanently" — a very different scenario, and a common accidental inversion when someone writes the happy path first out of habit. ## Constraints Mockito enforces at stub time Two validations fire when the stubbing is recorded, not when the mock is called, so failures point at the stubbing line: - **Checked exceptions must be declared.** If `send(...)` does not declare `IOException`, Mockito raises a `MockitoException` reading "Checked exception is invalid for this method". Unchecked exceptions and `Error`s are always allowed. If the real signature throws a wrapped `MailException` instead, stub that. - **`doNothing()` is void-only.** Applying it to a method with a return type raises "Only void methods can doNothing()". The value-returning analogue is `doReturn(...)`. You can also stub by exception *type* — `doThrow(IOException.class)` — which lets Mockito instantiate it; use the instance form when the test asserts on the message or cause. ## What doNothing() actually adds On a plain mock, void methods already do nothing: Mockito's default answer for `void` is a no-op, so a bare `doNothing().when(mailer).send(msg)` is documentation, not behaviour. It earns its place in exactly two situations. First, as a link in a consecutive chain like the one above, where it is the only way to say "stop throwing from here on". Second, on a spy, where an unstubbed void call would otherwise run the real body and hit the network, the database or the file system — `doNothing()` is what suppresses that. ## Pairing the stub with a verification The stub alone proves nothing about the retry policy: because the tail answer repeats, a component that gives up after one attempt and a component that retries a hundred times both produce a green test. Add the interaction assertion: ```java assertThat(service.dispatch(message)).isTrue(); verify(mailer, times(2)).send(any(Message.class)); ``` If the retry count is a policy value, assert exactly that count rather than `atLeastOnce()`, so an accidental change to the policy fails the test. ## Capturing arguments across the sequence When the retry is supposed to resend the *same* message (rather than rebuild it), an `ArgumentCaptor` with `times(2)` gives you both invocations' arguments and lets you assert they are equal — a cheap way to catch a retry that accidentally mutates or re-serializes its payload between attempts. ## Practical guidance Keep these chains to the shortest sequence that expresses a named scenario, give the test a name that says the scenario out loud (`resendsAfterTransientSmtpFailure`), and prefer a single well-chosen exception instance over generic `RuntimeException` so the test also documents which failures the policy treats as retryable.
- What happens if you write doNothing().doThrow(new IOException()).when(mailer).send(msg) instead?The first call succeeds and every call from the second onward throws, because the last entry in the chain is the sticky one. That models a collaborator that breaks permanently after one use, not a transient failure. Inverting the order is a common accidental bug when the happy path is written first out of habit.
- Is doNothing() ever required on a plain mock outside a chain?No. Mockito's default answer for a void method on a mock is already a no-op, so a standalone doNothing() is purely documentation. It becomes necessary as a link in a consecutive chain, and on a spy, where an unstubbed void call would run the real method body and cause real side effects.
The chain is a stack of instructions for a stand-in actor: the first note says 'collapse on cue', the last note says 'just stand there' — and once the stand-in reaches the last note, that is what they do for the rest of the show.
saying these in an interview costs you the question
- Trying to write when(mailer.send(msg)).thenThrow(...) for a void method
- Putting doNothing() first and doThrow(...) last, then expecting a transient failure
- Stubbing a checked exception the method does not declare and expecting it to work
- Believing the mock returns to throwing after the doNothing() entry is used
- Using doNothing() on a value-returning method