skip to content

Using Mockito's BDD API, how do you stub a method that returns void, or any method on a spy, and why can that call not be written in the same order as a normal value-returning stubbing?

level: middleimportance: should knowfreq 34%

answer

  1. void has no value to pass to given(...)
  2. given(spy.call()) runs the real method
  3. willX(...).given(mock).call() — behaviour first
  4. willDoNothing mainly for spies / overriding
  5. argument-last form loses compile-time return typing

basics

~20 s

Use the argument-last form: willDoNothing().given(spy).reindex(); willThrow(new IllegalStateException()).given(mailer).send(msg). A void call cannot be passed as an argument to given(...), and on a spy given(spy.call()) would run the real method first. The behaviour is stated before the call is recorded.

solid answer

~50 s

The normal form, `given(mock.call()).willReturn(v)`, works because the call is an expression Mockito can capture as an argument. That breaks in two cases. A void method has no value, so it cannot be passed to `given(...)` at all — it does not compile. A spy wraps a real object, so `given(spy.load())` actually executes `load()` before the stubbing is recorded. If the real method hits a database or throws, the test fails during setup. Both are solved by the argument-last family: `willReturn(v).given(spy).load()`, `willThrow(e).given(mock).send(msg)`, `willDoNothing().given(spy).flush()`, `willAnswer(inv -> ...).given(spy).call()`. Here `given(spy)` returns a stubbing proxy whose subsequent call is intercepted rather than executed, so the real method never runs. These map one-to-one onto the classic `doReturn/doThrow/doNothing/doAnswer(...).when(mock).call()` forms. Note that void methods already do nothing on a plain mock, so `willDoNothing()` matters mainly on spies or to override a previously stubbed throw.

code

java · 17 lines
java
@Spy SearchIndex index = new SearchIndex(realStore);
@Mock Mailer mailer;

@Test
void skipsReindexAndReportsMailFailure() {
    // the real reindex() would rebuild the whole index
    willDoNothing().given(index).reindex();

    // void method that must fail
    willThrow(new MailException("smtp down")).given(mailer).send(any(Message.class));

    // spy: never executes the real load()
    willReturn(Optional.of(doc)).given(index).load("doc-1");

    assertThatThrownBy(() -> service.publish("doc-1"))
            .isInstanceOf(PublishFailedException.class);
}

go deeper

for a junior

Recall the two forms and when each applies: given(call).willReturn for normal methods, willX(...).given(mock).call() for void methods and spies.

for a middle

Explain the mechanism — a void call cannot be an argument, and a spy dispatches unstubbed calls to the real object — and map the forms to doReturn/doThrow/doNothing.

for a senior

Add the trade-off that the argument-last form is not type-checked, plus chaining behaviours and overriding a stubbing for consecutive calls.

for a principal

Note that heavy reliance on spy stubbing usually signals a class doing too much, and the safer path is splitting it so plain mocks suffice.

## Why the normal form exists and where it stops working Mockito records stubbings by intercepting a call on the mock and remembering it. `given(mock.find(1)).willReturn(x)` works because `mock.find(1)` is evaluated first, gets intercepted, returns a placeholder, and `given(...)` then attaches the answer to the just-recorded invocation. Two situations break that mechanism. **Void methods.** `given(mock.flush())` cannot compile: `flush()` yields no value, so there is nothing to pass as an argument. Java's type system stops you before Mockito is involved. **Spies.** A spy delegates to a real instance unless a call is stubbed. `given(spy.load())` therefore *calls the real* `load()` during setup. That may hit a database, mutate state, take a long time, or throw — and if it throws, the test fails while arranging, not while acting. This is the single most common Mockito surprise for people used to plain mocks. ## The argument-last family BDDMockito answers both with forms that state the behaviour first and record the call last: - `willReturn(value).given(spy).load()` - `willThrow(new IllegalStateException()).given(mailer).send(msg)` - `willThrow(IllegalStateException.class).given(mailer).send(msg)` - `willDoNothing().given(spy).reindex()` - `willAnswer(invocation -> ...).given(spy).compute(anyInt())` - `willCallRealMethod().given(mock).helper()` The key is `given(spy)` — taking the mock itself, not a call on it. It returns a stubbing proxy; the method invoked on that proxy is captured and never dispatched to the real object. Argument matchers work as usual on that call. These are exact BDD renamings of the classic forms: `doReturn(v).when(spy).load()`, `doThrow(e).when(mock).send(msg)`, `doNothing().when(spy).reindex()`, `doAnswer(a).when(spy).compute(...)`. ## When willDoNothing is actually needed On a plain mock, void methods already do nothing — the default answer for `void` is a no-op — so stubbing them that way is redundant. It matters in two places: on a **spy**, where the real void method would otherwise run (skipping an expensive re-index or a real mail send); and to **override an earlier stubbing**, for example throw-then-succeed, written as `willThrow(e).willDoNothing().given(mock).send(msg)`. ## Consecutive and mixed behaviours The argument-last form chains too. `willReturn(1).willThrow(new IllegalStateException()).given(counter).next()` returns 1 on the first call and throws on the second and subsequent ones. The usual rule applies: once the list of answers is exhausted, the last one keeps being used. ## Practical guidance Use the ordinary `given(mock.call()).willReturn(...)` form by default — it reads better and type-checks the stubbed value against the method's return type. Switch to the argument-last form only when required: void methods, spies, or the rare case where real execution is unsafe. Note the trade-off consciously: `willReturn(...)` in the argument-last form is not type-checked against the method's return type, so a mismatch surfaces at runtime as a `WrongTypeOfReturnValue` error rather than at compile time. That is exactly why it should not become the default style. ## A related trap Because `given(spy.load())` runs the real method, people sometimes 'fix' the resulting exception by wrapping the setup in try/catch. That leaves the stubbing half-recorded and the mock in an odd state. The correct fix is always the argument-last form.

  • What actually goes wrong if you write given(spy.load("x")).willReturn(doc) on a spy?
    The real load("x") executes during setup, because a spy delegates to the underlying object for calls that are not yet stubbed. It may hit real infrastructure, mutate state or throw, and if it throws the test fails while arranging rather than while acting. The argument-last form willReturn(doc).given(spy).load("x") intercepts the call instead of dispatching it.
  • Is willDoNothing() needed for a void method on a plain mock?
    Usually not, because a mock's default answer for a void method is already a no-op. It is needed on spies, where the real method would otherwise run, and when overriding a previous stubbing such as throwing on the first call and succeeding afterwards.

saying these in an interview costs you the question

  • Trying to write given(mock.voidMethod()) and expecting it to compile
  • Not knowing that given(spy.call()) executes the real method
  • Using the argument-last form everywhere, losing compile-time checking of the stubbed return type
  • Adding willDoNothing() to every void call on a plain mock as boilerplate
  • Wrapping spy stubbing in try/catch to swallow the real method's exception

context