You create a Mockito mock of a Java interface and then call a method on it that you never stubbed. What does that call return, and what else happens?
answer
- RETURNS_DEFAULTS = ReturnsEmptyValues
- null / 0 / false; empty List, Map, Stream, Optional
- Integer null -> unboxing NPE
- chained call on unstubbed getter = NPE
- still recorded for verify()
basics
~20 sNo real code runs. Mockito returns the type's default value: null for objects, 0 for numbers, false for boolean, and an empty collection, Optional or stream for those types. Void methods do nothing. The call is recorded so you can verify it later.
solid answer
~50 sA mock created by `Mockito.mock()` is a generated object whose every method is intercepted; none of the real implementation runs. Unstubbed calls go to the default answer, `RETURNS_DEFAULTS`, which is implemented by `ReturnsEmptyValues`: primitives get their JVM default (`0`, `0.0`, `false`, `''`), primitive wrappers and every other object type get `null`, but well-known empty types are special-cased — `List`, `Set`, `Map`, `Stream`, `Optional` and friends come back empty rather than null. Void methods are no-ops. Two practical consequences: first, calling a method on a mock is safe, so you only stub what the test cares about; second, chaining off an unstubbed object-returning call gives a `NullPointerException`, which is the single most common surprise for beginners. Every call is still recorded in the mock's invocation list, so `verify()` sees it even though it returned a default.
code
java · 10 linesOrderRepository repo = Mockito.mock(OrderRepository.class);
assertNull(repo.findById(1L)); // object -> null
assertEquals(0, repo.countAll()); // int -> 0
assertFalse(repo.exists(1L)); // boolean -> false
assertTrue(repo.findAll().isEmpty()); // List -> empty, not null
assertTrue(repo.findLatest().isEmpty()); // Optional -> Optional.empty()
repo.deleteById(1L); // void -> no-op
verify(repo).deleteById(1L); // ...but still recordedgo deeper
Recall the table: null for objects, 0/false for primitives, empty collection or Optional, void does nothing — and that no real code runs.
Add the mechanism (RETURNS_DEFAULTS/ReturnsEmptyValues), the wrapper-unboxing trap, and that calls are still recorded for verification.
Talk about diagnosing the chained-call NPE, when RETURNS_SMART_NULLS earns its keep during debugging, and why you would not make throwing-on-unstubbed the team default.
Frame defaults as a policy choice: permissive defaults keep tests focused on one behaviour, strict answers surface hidden coupling; pick per-codebase and prefer fixing the design over tuning the default answer globally.
## What a mock actually is When you write `PaymentGateway gateway = Mockito.mock(PaymentGateway.class);` Mockito generates a new class at runtime that implements (or extends) the given type and routes every method call into its own interception machinery instead of the real body. So there is no partially-real object here: the mock has no meaningful state, no constructor logic, and no behaviour beyond what you give it. Because the mock still has to return *something* when a method has a return type, Mockito consults the mock's **default answer**. Unless you say otherwise, that answer is `Answers.RETURNS_DEFAULTS`, whose implementation class is `ReturnsEmptyValues`. ## The default value table `ReturnsEmptyValues` resolves an unstubbed call like this: - **Primitives** get the JVM default: `int`/`long`/`short`/`byte` → `0`, `double`/`float` → `0.0`, `boolean` → `false`, `char` → `''`. - **Primitive wrappers** (`Integer`, `Boolean`, …) → `null`. This asymmetry bites: a method declared `int count()` returns `0`, but `Integer count()` returns `null`, and auto-unboxing that into an `int` throws `NullPointerException`. - **Known empty-able types** → an empty instance, not null: `Collection`, `List`, `Set`, `SortedSet`, `Map`, `SortedMap`, `Iterable`, `Iterator`, `Stream`, `IntStream`/`LongStream`/`DoubleStream`, `Optional`, `OptionalInt`/`OptionalLong`/`OptionalDouble`, and `Duration`/`Period` (zero). Each call returns a fresh empty instance, so you must not rely on identity between two calls. - **Everything else** (your domain classes, `String`, arrays) → `null`. - **`void`** → nothing happens at all; the call simply returns. A handful of methods are handled specially rather than defaulted: `equals()` and `hashCode()` cannot be stubbed or intercepted (Mockito needs them for its own bookkeeping), and `toString()` returns a readable name such as `Mock for PaymentGateway, hashCode: 12345`. ## Why empty collections instead of null Returning an empty list is a deliberate ergonomic choice: production code overwhelmingly iterates over returned collections, and a null would blow up in code under test that is behaving perfectly correctly. The empty value keeps the *unspecified* parts of a collaborator harmless so your test only has to describe the interesting part. ## The classic failure it causes ```java OrderRepository repo = mock(OrderRepository.class); // findById is unstubbed -> returns null String id = repo.findById(1L).getCustomerId(); // NullPointerException ``` The NPE is not a Mockito bug; it is Mockito telling you that you never described what `findById` should do. The fix is to stub it (`when(repo.findById(1L)).thenReturn(order)`), not to work around the null. When such NPEs are frequent and hard to trace, you can create the mock with `Answers.RETURNS_SMART_NULLS`, which returns a placeholder object that throws a `SmartNullPointerException` naming the unstubbed method and the line where the mock was used — a much better diagnostic. ## Changing the default deliberately The default answer is a creation-time setting, so it is chosen where the mock is made: - `mock(Foo.class, Answers.RETURNS_SMART_NULLS)` — diagnostic nulls, useful while debugging. - `mock(Foo.class, Answers.RETURNS_MOCKS)` — every object-returning method returns another mock instead of null. - `mock(Foo.class, Answers.RETURNS_DEEP_STUBS)` — like `RETURNS_MOCKS`, but the chain is remembered so `when(a.getB().getC()).thenReturn(x)` works. - `mock(Foo.class, Answers.CALLS_REAL_METHODS)` — unstubbed calls execute the real body. - `mock(Foo.class, withSettings().defaultAnswer(myAnswer))` — a custom `Answer` implementation, e.g. one that throws for anything unstubbed so no accidental default can slip through. - With the annotation form: `@Mock(answer = Answers.RETURNS_SMART_NULLS)`. Using a strict custom answer that throws is occasionally used on legacy code to force every interaction to be made explicit; the cost is noisy tests, so it is a temporary tool rather than a default. ## Defaults and verification Returning a default does not mean the call was ignored. Every invocation on a mock is appended to its invocation list, so `verify(gateway).charge(amount)` passes even though `charge` was never stubbed and returned `false`/`null`. This is exactly why stubbing and verification are separable: you stub what the code needs to keep running, and you verify what the code was supposed to do. ## What to say in an interview State the rule (nulls/zeros/false, empty collections and `Optional`), name `RETURNS_DEFAULTS`/`ReturnsEmptyValues`, mention the wrapper-unboxing trap and the chained-call NPE, and finish with the fact that the call is still recorded for verification. That is a complete answer at any level.
- A test throws NullPointerException on a line that only calls a method on the result of a mocked call. What is the cause and the fix?The inner method was never stubbed, so it returned the default `null`, and the outer call dereferenced it. The fix is to stub the inner call with `when(...).thenReturn(...)` (or return an `Optional`/empty value the code can handle), not to null-guard production code for the test's benefit. If this happens often while exploring an unfamiliar codebase, create the mock with `RETURNS_SMART_NULLS` so the failure message names the unstubbed method.
- How would you make every unstubbed method on one particular mock throw instead of returning a default?Pass a custom `Answer` as the default answer at creation: `mock(Foo.class, withSettings().defaultAnswer(inv -> { throw new UnsupportedOperationException(inv.getMethod().getName()); }))`. It is a per-mock setting, so it does not affect other mocks. It is useful to smoke out hidden interactions in legacy code, but as a permanent default it makes tests brittle and verbose.
- Does returning a default value mean Mockito did not record the call?No. Every invocation on a mock is recorded in its invocation list regardless of whether it was stubbed, so `verify()` and `InOrder` see unstubbed calls too. Stubbing controls what comes back; recording is unconditional. That is why you can verify a void method that by definition returns nothing.
A mock is a stand-in actor who has been handed only the lines you wrote. Ask anything else and they shrug: silence for void, an empty hand for collections, nothing at all for objects.
saying these in an interview costs you the question
- Saying an unstubbed method throws an exception or fails the test by default
- Saying the real implementation runs on a mock created with mock()
- Claiming a mocked method returning List gives null (it gives an empty list)
- Assuming every method must be stubbed before it can be called
- Thinking an unstubbed call is not recorded and therefore cannot be verified