skip to content

Explain how you tell a Mockito mock to return a specific value for a given method call, and what such a mock returns for methods you never stubbed.

level: juniorimportance: must knowfreq 78%

answer

  1. when(mock.call(args)).thenReturn(value)
  2. thenThrow(instance) or thenThrow(Class) via no-arg ctor
  3. thenCallRealMethod for the real body
  4. unstubbed -> null / 0 / false / empty collection
  5. missing stub shows up as an NPE in production code

basics

~10 s

Write when(mock.method(args)).thenReturn(value), or thenThrow(exception) for a failure path. Anything you do not stub returns the type's default: null for objects, 0 for numbers, false for boolean, and an empty collection for collection-returning methods.

solid answer

~40 s

Stubbing reads as `when(mock.someCall(args)).thenReturn(value)`: ```java UserRepository repo = mock(UserRepository.class); when(repo.findById(1L)).thenReturn(new User(1L, "ada")); when(repo.findById(99L)).thenThrow(new NotFoundException()); ``` The arguments in the `when(...)` call form the match: the stub fires only for calls whose arguments are equal (by `equals`) to those, or that satisfy the argument matchers you used instead of literals. `thenThrow` sets the error path - either an instance or a class, in which case Mockito constructs it with the no-arg constructor. `thenCallRealMethod()` routes to the real implementation. Unstubbed methods do not fail. A default Mockito mock returns the zero value for the return type: `null` for object types, `0`/`0.0` for numbers, `false` for `boolean`, and empty `List`/`Set`/`Map`/`Optional.empty()` for collection-ish types. That is why a forgotten stub usually shows up as a NullPointerException in the code under test rather than as a Mockito error.

code

java · 9 lines
java
UserRepository repo = mock(UserRepository.class);

when(repo.findById(1L)).thenReturn(new User(1L, "ada"));
when(repo.findById(99L)).thenThrow(new NotFoundException("no user 99"));

assertEquals("ada", repo.findById(1L).getName());
assertThrows(NotFoundException.class, () -> repo.findById(99L));
assertNull(repo.findById(2L));          // unstubbed object return
assertEquals(List.of(), repo.findAll()); // unstubbed collection return

go deeper

for a junior

Show the syntax confidently and state the defaults for unstubbed calls: null, 0, false, empty collections.

for a middle

Add matching semantics (equals or matchers, all-or-nothing), the thenThrow class-versus-instance difference, and why a missing stub shows up as an NPE.

for a senior

Connect defaults to diagnosis and test hygiene: stub only what is needed, prefer specific arguments over any(), let strict stubs catch dead stubbings.

for a principal

Discuss what stubbing says about design - how many stubs a test needs is a proxy for a collaborator's chattiness - and where fakes beat piles of stubs.

## The shape of a stub Mockito's core stubbing form is: ```java when(mock.methodCall(arguments)).thenReturn(value); ``` Read it as "when this call happens, then return this". The chain after `when(...)` is called the `OngoingStubbing`, and it offers `thenReturn`, `thenThrow`, `thenCallRealMethod`, and `thenAnswer`. ## Matching The arguments you write inside `when(...)` define which calls the stub covers. Literals are compared with `equals()`, so `when(repo.findById(1L))` matches only the call with `1L`. Matchers such as `any()`, `eq()`, `argThat(...)` broaden that. The rule to remember early: if you use a matcher for one argument you must use matchers for all of them - mixing a raw literal with a matcher raises `InvalidUseOfMatchersException`. A call that matches no stubbing simply falls back to the mock's default answer. ## thenReturn `thenReturn(value)` stores a single already-computed value. The expression you pass is evaluated once, at stubbing time, and the same reference is returned to every matching call - which matters if the object is mutable and the code under test mutates it. Type compatibility is enforced by the compiler through generics in the common case, so a mismatch is usually a compile error rather than a runtime surprise. ## thenThrow `thenThrow(new IllegalStateException("boom"))` makes the matching call throw. There is also `thenThrow(IllegalStateException.class)`, where Mockito instantiates the exception itself using the no-arg constructor - convenient, but it produces an exception with no message, which makes a failing assertion less informative. There is one rule Mockito enforces: a **checked** exception may only be stubbed if the method declares it (or a supertype of it) in its `throws` clause. Otherwise you get "Checked exception is invalid for this method". Unchecked exceptions and `Error`s are always allowed. The usual test then asserts on it: ```java when(repo.findById(99L)).thenThrow(new NotFoundException()); assertThrows(NotFoundException.class, () -> service.load(99L)); ``` ## thenCallRealMethod `thenCallRealMethod()` tells the mock to execute the real body for that method, turning the mock into a partial mock for that one call. It requires a real implementation to exist, so it does not work for interface or abstract methods. ## What an unstubbed method returns Every Mockito mock has a default answer, `RETURNS_DEFAULTS`, which produces the type's zero value: - object types -> `null` - `int`, `long`, `double`, ... -> `0` - `boolean` -> `false` - `List`, `Set`, `Map`, `Collection`, `Stream`, `Optional` -> empty instances, not `null` This is deliberate: a mock is usable straight out of `mock(...)` and you stub only what the test cares about. The practical consequence is that a missing stub does not announce itself. The code under test receives `null`, dereferences it, and you debug an NPE that points at your production code rather than at the test. When a test fails with an NPE on a collaborator's result, check for a missing or mis-matched stub first. Alternative default answers exist for other trade-offs, but the plain default is what you get from `mock(Foo.class)` and from `@Mock`. ## Practical habits - Stub in the arrange section, assert in the assert section - do not scatter `when(...)` through the test. - Stub only what the test needs; with strict stubs, unused stubbings are reported as errors, which keeps tests honest. - Prefer specific argument values over `any()` when the arguments are part of what you are asserting; a stub that matches everything can hide a bug in how the arguments are built. - Remember stubbing is per mock instance: two mocks of the same interface share nothing.

  • Your test fails with a NullPointerException inside the class under test, not in the test itself. How does that relate to stubbing?
    Most often a collaborator call was never stubbed, so the mock returned null and the production code dereferenced it. Check which mock call happens on that line and whether the arguments actually match a stubbing you wrote - an argument mismatch has the same effect as no stub at all. Adding the missing stub, or fixing the matcher, resolves it.
  • What is the difference between thenThrow(new IllegalStateException("boom")) and thenThrow(IllegalStateException.class)?
    The first throws exactly the instance you built, message and all. The second asks Mockito to instantiate the exception via its no-arg constructor, so the thrown exception has no message and the class must actually have such a constructor. The instance form is preferable when the message or any field matters to the assertion or to the failure output.

saying these in an interview costs you the question

  • Expecting an unstubbed mock method to throw or fail loudly instead of returning a default
  • Thinking unstubbed collection-returning methods give null - they return empty collections
  • Mixing raw argument values and matchers in the same when(...) call
  • Believing a stub on one mock instance affects another mock of the same type
  • Assuming thenReturn re-evaluates its argument expression on each call

context