skip to content

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?

level: middleimportance: should knowfreq 34%

answer

  1. placeholder instead of null
  2. SmartNullPointerException on first use
  3. message names the unstubbed call
  4. String → "", not null
  5. diagnostic setting, not strictness

basics

~20 s

Instead 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
java
@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

for a junior

Know that it swaps null for a placeholder that throws a clear error naming the missing stub.

for a middle

Add the mechanics: mockable return types only, ReturnsMoreEmptyValues fallback, throws only when the value is used.

for a senior

Position it as a targeted diagnostic and articulate why null-checking code behaves differently under it.

for a principal

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

context