Mockito's Answers.RETURNS_SMART_NULLS changes what an unstubbed mock method hands back. What does it return, what is a SmartNullPointerException, and when would you turn it on?
answer
- placeholder instead of null
- SmartNullPointerException on first use
- message names the unstubbed call
- String → "", not null
- diagnostic setting, not strictness
basics
~20 sInstead of plain null it returns a placeholder object. Dereferencing that placeholder throws SmartNullPointerException, whose message names the mock and the exact unstubbed call that produced it. Turn it on while debugging an NPE caused by a missing stub.
solid answer
~50 s`RETURNS_SMART_NULLS` replaces the plain `null` that `RETURNS_DEFAULTS` gives for object return types with a **smart null**: a lightweight mock of the return type that does nothing except throw `SmartNullPointerException` the moment any method is called on it. The message says which mock and which unstubbed method produced the value, and carries the stack trace of that original call — so the failure points at the *missing stubbing*, not at the line that happened to dereference the null. It also uses `ReturnsMoreEmptyValues`, so `String` comes back as `""` and arrays as zero-length rather than null. Limits: a smart null can only be created for a mockable type — final types under the subclass mock maker, primitives and a few JDK types fall back to ordinary defaults. And it only fires when the value is *used*; a null that is merely stored or compared stays silent. Use it as a diagnostic setting on a specific mock, not blanket policy: `@Mock(answer = Answers.RETURNS_SMART_NULLS)`.
code
java · 12 lines@Mock(answer = Answers.RETURNS_SMART_NULLS)
OrderRepository repo;
@Test
void smartNullNamesTheMissingStub() {
OrderService service = new OrderService(repo);
// repo.findById(42) was never stubbed -> returns a smart null
// service calls order.total() on it:
assertThrows(SmartNullPointerException.class,
() -> service.totalFor(42L));
}go deeper
Know that it swaps null for a placeholder that throws a clear error naming the missing stub.
Add the mechanics: mockable return types only, ReturnsMoreEmptyValues fallback, throws only when the value is used.
Position it as a targeted diagnostic and articulate why null-checking code behaves differently under it.
Weigh a build-wide default answer against explicit stubbing plus strict stubs, including the cost of tests diverging from production null semantics.
## The problem it solves With Mockito's normal default answer, any unstubbed method returning an object gives back `null`. Nothing complains at that moment. The class under test then does something with the value — `user.getName()`, `result.total()` — and blows up with a bare `NullPointerException` several frames away. The stack trace shows production code; the actual defect is a stubbing you forgot to write, and nothing in the failure tells you which one. `RETURNS_SMART_NULLS` closes that gap. ## What a smart null is When the default answer is `Answers.RETURNS_SMART_NULLS` (implemented by `ReturnsSmartNulls`), an unstubbed call whose return type is mockable produces a small proxy of that type instead of `null`. The proxy has one job: any method invoked on it throws `SmartNullPointerException`, a subclass of Mockito's reporting errors. The message reads roughly *"You have a NullPointerException here: ... because this method call was not stubbed: repo.findById(42)"* and includes the location where the unstubbed call happened. Its `toString()` is also overridden to say it is a SmartNull from that call, so a value that ends up in an assertion message or a log identifies itself. For return types that cannot be proxied — primitives, and object types the active mock maker cannot handle — it falls back to `ReturnsMoreEmptyValues`, a slightly more generous version of the standard empty values: `0`/`false` as usual, but `""` for `String` and a zero-length array for array types instead of `null`. ## How to enable it Per mock, three ways: - `@Mock(answer = Answers.RETURNS_SMART_NULLS) OrderRepository repo;` - `mock(OrderRepository.class, Answers.RETURNS_SMART_NULLS)` - `mock(OrderRepository.class, withSettings().defaultAnswer(Answers.RETURNS_SMART_NULLS))` There is also a global route (a `MockitoConfiguration` class implementing `IMockitoConfiguration` with `getDefaultAnswer()` returning smart nulls), which the Mockito authors themselves once suggested might become the default. It never did, and turning it on build-wide has real costs: every unstubbed call now allocates a proxy, and tests that legitimately expect `null` from a collaborator start behaving differently. ## What it does not do It is not a strictness mechanism. It only fires when something *calls a method* on the returned value. Code that stores the value, passes it along, compares it with `==`, or null-checks it will never trigger the exception — the smart null is simply a non-null object, which incidentally means `if (x == null)` branches take the *other* path than they would with plain defaults. That behaviour difference is exactly why smart nulls are best used as a temporary diagnostic on the mock you are investigating rather than as a permanent global setting. It also does not detect *unnecessary* stubbings or argument mismatches — that is `Strictness.STRICT_STUBS`, and the two are complementary: strict stubs catches "you stubbed something that was never called" and "you called with different arguments"; smart nulls catch "you never stubbed something the code needed". ## Practical workflow A productive debugging loop when a mock-heavy test dies on an NPE: 1. switch the suspect mock to `RETURNS_SMART_NULLS`; 2. re-run — the failure now names the unstubbed call; 3. add the stubbing; 4. revert the mock to the default answer so the test does not depend on the diagnostic mode. Some teams keep it on permanently for repository-style collaborators whose methods never legitimately return `null`, and that is defensible; keeping it on for collaborators that *do* return `null` meaningfully (a cache lookup, a `Map.get`) is not, because the test then diverges from production behaviour in a way nobody notices.
- A method under test does `if (repo.find(id) == null) { ... }`. How does RETURNS_SMART_NULLS change its behaviour?The smart null is a real object, so the `== null` check is false and the code takes the non-null branch — the opposite of what plain defaults would do. No exception is thrown either, because no method was called on the value. That is a good illustration of why smart nulls are a debugging aid rather than a drop-in default: they change control flow in null-checking code.
- How do smart nulls relate to STRICT_STUBS?They cover opposite failure modes. Strict stubs fails a test for stubbings that were declared but never used, and warns about calls that matched the method but not the arguments. Smart nulls fire when code uses a value from a call that was never stubbed at all. Running both gives you coverage on both sides of the stubbing/usage mismatch.
saying these in an interview costs you the question
- Saying it makes every unstubbed call throw immediately (it throws only on use)
- Confusing it with STRICT_STUBS or unnecessary-stubbing detection
- Believing it works for primitive return types
- Assuming null-checks behave the same as with RETURNS_DEFAULTS
- Recommending it as a global default without mentioning the behaviour change