You create a collaborator with Mockito.mock(SomeService.class) and never stub anything on it. What do its methods return, and what role is that unstubbed object playing in the test?
answer
- default answer = RETURNS_DEFAULTS / ReturnsEmptyValues
- null objects, 0/false primitives, empty collections, Optional.empty()
- void methods: no-op
- unused collaborator = placeholder; consumed defaults = implicit stub
- SMART_NULLS names the missing stub; DEEP_STUBS is a smell
basics
~20 sNothing throws. Mockito's default answer returns null for object types, 0/false for primitives, and empty collections or Optional.empty() for those types. If the test never uses those values, the object is just a placeholder to satisfy a constructor; if the code consumes them, it is a stub returning defaults.
solid answer
~50 sAn unstubbed Mockito mock is not inert and does not blow up. Its default answer, `RETURNS_DEFAULTS`, gives back the type's empty value: `null` for arbitrary objects, `0`/`false` for primitives and their boxes, an empty `List`/`Set`/`Map`, `Optional.empty()`, and an empty `Stream`. The role depends on use. If the object exists only to satisfy a constructor parameter and is never touched, it is a placeholder — passed for compilation, irrelevant to the outcome. The moment the code under test consumes those default values, the object is supplying canned responses, and the test's behaviour now depends on defaults nobody wrote down. That implicitness is the risk: a `null` return that silently drives an `else` branch, or an empty list that makes a loop a no-op, reads as "nothing configured" but is really untracked behaviour. Other answers exist — `RETURNS_SMART_NULLS` fails with a helpful message instead, and `RETURNS_DEEP_STUBS` chains — but the honest fix is usually to stub explicitly what the test relies on.
code
java · 13 linesUserRepository repo = mock(UserRepository.class);
assertNull(repo.findRaw(1L)); // reference type -> null
assertEquals(0, repo.countActive()); // primitive -> 0
assertTrue(repo.findAll().isEmpty()); // collection -> empty list
assertTrue(repo.findById(1L).isEmpty()); // Optional -> Optional.empty()
// Green for the wrong reason: the empty default drives the branch,
// and nothing in the test says so.
assertThrows(UserNotFoundException.class, () -> service.displayName(1L));
// Say it out loud instead:
when(repo.findById(1L)).thenReturn(Optional.empty());go deeper
Recall the default values — null, zero, false, empty collections, Optional.empty() — and that void methods do nothing.
Explain the difference between an untouched placeholder and defaults the code actually consumes, and why the second deserves an explicit stub.
Discuss tests that are green for the wrong reason, using SMART_NULLS as a diagnostic, and treating DEEP_STUBS as a design signal.
Set the convention: assertions may not depend on undeclared defaults, deep stubs are banned by review, and the default-answer choice is a suite-wide decision rather than a per-test trick.
## Default answers, concretely `Mockito.mock(Type.class)` returns a generated subclass or proxy in which every overridable method is intercepted. With no stubbing, calls go to the default `Answer`, `ReturnsEmptyValues` (exposed as `Answers.RETURNS_DEFAULTS`), which produces: - `null` for arbitrary reference types - `0`, `0L`, `0.0`, `false` for primitives and their wrappers - an empty, usually immutable `List`, `Set`, `Map`, `Collection`, `Iterable` - `Optional.empty()`, and empty `Stream`/`IntStream` etc. - `void` methods do nothing at all So the mock never throws "you forgot to stub me" on its own. Strict stubbing (`Strictness.STRICT_STUBS`) catches *unused* and *mismatched* stubbings, but it does not require every called method to be stubbed. That asymmetry is what surprises people. ## What role that object plays The object's role is decided by whether its output matters: **Nobody touches it.** A constructor takes five collaborators and this test exercises a path that uses two. The other three are passed only so the object can be built. They are placeholders — nothing about them influences the outcome, and the test would read the same if they were `null` (which you cannot pass safely if the constructor validates). **The code consumes the defaults.** Now the object is answering calls, and the answers are canned — they just happen to be canned by Mockito rather than by you. `findById` returns `null`, `findAll` returns an empty list, `isEnabled` returns `false`. The test passes because of behaviour nobody declared, and the next reader cannot tell whether the empty result was intended or accidental. That second case is where the everyday bugs live: - A `null` from an unstubbed lookup silently steers the code into an error branch, and the test asserts the error branch — appearing to prove behaviour that was never really exercised. - An empty list makes a loop iterate zero times, so a test that claims to check per-item processing checks nothing. - A `false` from an unstubbed feature-flag check disables the feature under test. In all three, the test is green for the wrong reason. The rule that follows: if the test's outcome depends on a value, stub that value explicitly, even when the default happens to be what you want. Explicit stubbing is documentation. ## Other default answers, and what they are for - **`RETURNS_SMART_NULLS`** — instead of a bare `null`, returns a placeholder that throws a `SmartNullPointerException` naming the unstubbed call when it is dereferenced. It converts a mystery NPE deep in production code into "you did not stub `orderRepository.find()` at line 42". Excellent while debugging; teams often do not adopt it globally, preferring strict stubbing plus explicit stubs. - **`RETURNS_DEEP_STUBS`** — `mock(A.class, RETURNS_DEEP_STUBS)` lets `a.getB().getC().getName()` work without stubbing each hop. Convenient, and a strong smell: it usually means the test is reaching through a chain that violates the Law of Demeter, and it hides which object is actually being exercised. - **`RETURNS_MOCKS`** — returns further mocks instead of nulls where possible. - **`CALLS_REAL_METHODS`** — runs the real implementation, moving the object toward partial-double territory; a `spy` is the normal way to express that intent. - A custom `Answer` — a lambda computing the return value from the arguments, which is how people hand-build behaviour when a canned value is not enough. ## Practical guidance 1. Stub the calls whose values your assertions depend on, even for `Optional.empty()` or an empty list; state the intent instead of inheriting it. 2. Leave genuinely unused collaborators unstubbed — do not stub "just in case", because strict stubbing will flag unnecessary stubbings and that flagging is valuable feedback. 3. When a test fails with a confusing NPE, temporarily switching that mock to `RETURNS_SMART_NULLS` usually names the missing stub in one run. 4. Treat a need for `RETURNS_DEEP_STUBS` as a design signal about the call chain rather than a testing convenience. Interviewers ask this because the answer reveals whether a candidate knows what their doubles are actually doing when nobody configured them — a surprisingly common source of tests that pass while proving nothing.
- A test passes, but you suspect it passes because an unstubbed method returned an empty Optional. How do you confirm that quickly?Recreate that collaborator with RETURNS_SMART_NULLS, or add the explicit stub you believe is implied and see whether anything changes. Smart nulls throw a SmartNullPointerException naming the unstubbed invocation when the value is used, which points straight at the call. Adding the explicit stub is the permanent fix, because it records the intent even when the value matches the default.
- Why is RETURNS_DEEP_STUBS usually discouraged?It silently fabricates a chain of mocks so that expressions like a.getB().getC() work without any stubbing, which hides which object the test actually depends on and produces failures far from their cause. It also normalises long train-wreck call chains in production code, so the convenience masks a design problem that would otherwise show up as painful test setup.
saying these in an interview costs you the question
- Believing an unstubbed mock throws or fails the test when called
- Thinking strict stubbing forces you to stub every method that gets called
- Assuming unstubbed collection-returning methods yield null (true only in very old Mockito)
- Stubbing everything 'just in case', then fighting UnnecessaryStubbingException
- Reaching for RETURNS_DEEP_STUBS as a normal convenience