skip to content

Advanced Mocking

The machinery beyond plain instance mocks: static and constructor mocking, the inline mock maker that unlocks final types, deep stubs for fluent chains, and custom default answers. Interviewers ask these to gauge whether you know both the capability and the design cost of reaching for it.

on this pageshow

explore

questions

23

In a Mockito test, what value comes back when you call a method on a mock that you never stubbed, and what part of Mockito decides that value?

level: juniorimportance: must knowfreq 62%

answer

  1. unstubbed → default answer, not an error
  2. RETURNS_DEFAULTS = ReturnsEmptyValues
  3. 0 / false / empty collection / Optional.empty / null
  4. String returns null, not ""
  5. strictness ≠ default answer

basics

~10 s

The mock's default answer decides. Mockito's built-in default, Answers.RETURNS_DEFAULTS, gives 0 for numeric primitives, false for boolean, an empty collection, Optional or Stream for those types, and null for every other object. Nothing throws.

solid answer

~50 s

Every Mockito mock carries a **default answer** — a strategy consulted whenever an invocation matches no stubbing. Out of the box that is `Answers.RETURNS_DEFAULTS`, implemented by `ReturnsEmptyValues`: numeric primitives yield `0`, `boolean` yields `false`, `void` does nothing, known collection types yield an empty collection, `Optional` yields `Optional.empty()`, `Stream` yields an empty stream, and everything else — including `String` — yields `null`. The important consequence is that an unstubbed call is *legal*: the mock answers instead of complaining, so a forgotten stub usually surfaces later as a `NullPointerException` deep in the code under test rather than as a Mockito error. Strictness (`STRICT_STUBS`) reports *unused* stubbings and argument mismatches; it does not change what an unstubbed method returns. You can swap the strategy per mock — `mock(Foo.class, Answers.RETURNS_SMART_NULLS)`, `@Mock(answer = ...)`, or `withSettings().defaultAnswer(...)` — including with your own `Answer` implementation.

code

java · 15 lines
java
interface Repo {
    User findById(long id);
    List<User> findAll();
    Optional<User> findByEmail(String email);
    int count();
    boolean exists(long id);
}

Repo repo = Mockito.mock(Repo.class);

assertNull(repo.findById(1L));
assertTrue(repo.findAll().isEmpty());
assertTrue(repo.findByEmail("[email protected]").isEmpty());
assertEquals(0, repo.count());
assertFalse(repo.exists(1L));

go deeper

for a junior

Know the value table: 0, false, empty collection/Optional, null for everything else, and that no exception is thrown.

for a middle

Explain that the value comes from a per-mock Answer (RETURNS_DEFAULTS / ReturnsEmptyValues) and that strictness is a separate concern.

for a senior

Use it diagnostically — read an NPE inside production code as a probable missing stub — and know how to swap the default per mock.

for a principal

Have a position on team-wide policy: whether a project-wide custom default answer is worth the hidden coupling, versus explicit stubbing plus strict stubs.

## What a default answer is A Mockito mock is an object whose methods are all intercepted. On each call Mockito looks through the stubbings you registered (`when(...).thenReturn(...)`, `doReturn(...).when(mock).m()`) for one whose method and argument matchers fit the invocation. If one fits, it produces the value. If none fits, Mockito falls back to the mock's **default answer**: an object implementing `org.mockito.stubbing.Answer<Object>` that is asked to produce a value for the invocation. That fallback is a per-mock setting, not a global constant. `Answers` is an enum of ready-made implementations — `RETURNS_DEFAULTS`, `RETURNS_SMART_NULLS`, `RETURNS_MOCKS`, `RETURNS_DEEP_STUBS`, `RETURNS_SELF`, `CALLS_REAL_METHODS` — and every one of them is just an `Answer` you could have written yourself. ## What RETURNS_DEFAULTS actually returns `RETURNS_DEFAULTS` delegates to `ReturnsEmptyValues`: - numeric primitives and their boxes → `0` / `0L` / `0.0` (never `null` for a primitive return, which would be impossible to unbox); - `boolean`/`Boolean` → `false`; - `char` → ``; - `void` → nothing happens; - known collection types (`List`, `Set`, `Map`, `Collection`, `Iterable`, and their common subtypes) → an empty instance, so callers can iterate safely; - `Optional` → `Optional.empty()`, `OptionalInt`/`OptionalLong`/`OptionalDouble` likewise; `Stream` → an empty stream; - everything else, including `String`, arrays, and your own domain types → `null`. The empty-collection and empty-`Optional` behaviour is deliberate: those are the return types where `null` is almost always a bug in the *test*, not a realistic value from the real collaborator. ## Why this matters in practice Because unstubbed calls are legal, the failure mode of a missing stub is displaced. You call `service.handle(cmd)`, the service asks `repository.findById(id)`, gets `null` from the mock, and blows up with an NPE inside the service — a stack trace that points at your production code even though the defect is a missing stub. Recognising that pattern ("NPE on a value that came from a collaborator" = missing stub) is a large part of reading Mockito failures quickly. The same rule explains a common surprise with mocked builders and fluent APIs: `mock.withX().withY()` NPEs on the second call because `withX()` returned `null`, not the mock. That is what `RETURNS_SELF` exists for. ## What does *not* change it Strictness is a separate axis. `Strictness.STRICT_STUBS` (via `MockitoExtension`, `@MockitoSettings`, or `mockitoSession()`) fails the test for stubbings that were never used and for calls that *almost* matched a stubbing with different arguments. It never turns an unstubbed call into an error and never changes the returned value. If you want an unstubbed call to be loud, you supply a default answer that throws, or use `RETURNS_SMART_NULLS` so that *using* the result is loud. Spies are the other side of the coin: a spy created with `Mockito.spy(realObject)` runs the real method when nothing is stubbed, because its default answer is effectively "call the real thing". ## Setting a different default Three equivalent entry points: - annotation: `@Mock(answer = Answers.RETURNS_SMART_NULLS) Repository repo;` - factory overload: `mock(Repository.class, Answers.RETURNS_SMART_NULLS)`; - settings: `mock(Repository.class, withSettings().defaultAnswer(RETURNS_SELF).name("repo"))`, which is the form you need when you also want a name, extra interfaces, or a custom `Answer` lambda. A project-wide default is possible by putting a class named `MockitoConfiguration` implementing `IMockitoConfiguration` on the test classpath and overriding `getDefaultAnswer()`, but it is discouraged: it silently changes the meaning of every mock in the build, including in modules whose authors never read that class.

  • A test fails with a NullPointerException inside the class under test, not in the test itself. How does the default-answer rule help you diagnose it?
    The first thing to check is whether the null came out of a mock. Any collaborator method you did not stub returns null for object types, so the class under test happily receives null and dereferences it. Trace the NPE'd value back to its source: if it originated from a mock call, the fix is a missing stubbing, not defensive code in production.
  • Does Strictness.STRICT_STUBS make unstubbed calls fail?
    No. Strict stubs detect unused stubbings and argument mismatches — a stubbing you registered but never triggered, or a call that hit the same method with different arguments. A call to a method that was never stubbed at all is still answered by the default answer and returns 0/false/empty/null. To make unstubbed calls fail you need a default answer that throws.
  • Why does Mockito return an empty list rather than null for a method returning List?
    Because null is essentially never the useful value there — production code iterates the result, and a null would only produce an NPE that teaches you nothing. Returning an empty collection lets the code under test run to the point where the real assertion fails, which is more informative. The same reasoning gives Optional.empty() and an empty Stream.

saying these in an interview costs you the question

  • Claiming an unstubbed method throws an exception by default
  • Saying unstubbed String returns an empty string (it returns null)
  • Believing STRICT_STUBS makes unstubbed calls fail
  • Thinking a mock runs the real method when not stubbed (that is a spy)
  • Saying the default answer is global rather than per-mock

context

open as a page

What does Mockito's Answers.RETURNS_DEEP_STUBS do, how do you turn it on for a mock, and which failure does it exist to prevent?

level: middleimportance: must knowfreq 38%

basics

~20 s

It makes a mock auto-create and cache a mock for each mockable return type, so chained calls like when(a.getB().getC()).thenReturn(x) work instead of throwing NullPointerException on the second link. Enable with @Mock(answer = Answers.RETURNS_DEEP_STUBS) or mock(A.class, RETURNS_DEEP_STUBS).

open as a page

Older Mockito versions could not mock a final class or a final method. Explain the mechanism behind that limitation and what the inline mock maker does differently.

level: middleimportance: must knowfreq 58%

basics

~20 s

The classic mock maker generates a runtime subclass and overrides methods, and Java forbids subclassing a final class or overriding a final method. The inline mock maker instead attaches a Java agent and rewrites the loaded class's own bytecode, so no subclass is needed.

open as a page

A class you are testing creates a collaborator with `new` inside one of its methods, so there is no seam to inject a test double. How does Mockito's `mockConstruction(Class)` API let you take control of that instance, and what exactly does it intercept?

level: middleimportance: must knowfreq 42%

basics

~20 s

Mockito.mockConstruction(Foo.class) opens a scope in which every new Foo(...) on the current thread returns a Mockito mock instead of a real object, and the real constructor body never runs. Closing the scope restores normal construction.

open as a page

Your code calls a static utility method that returns the current time, and you need it to return a fixed value inside one test. How do you stub that static call with Mockito, and how do you keep the stubbing from leaking outside the test?

level: middleimportance: must knowfreq 50%

basics

~20 s

Open Mockito.mockStatic(TheClass.class) in a try-with-resources block; inside it, stub with mocked.when(TheClass::method).thenReturn(value). While the scope is open all static methods of that class are mocked on the current thread, and closing the scope restores the real implementations.

open as a page

A test suite starts failing with a Mockito error saying static mocking is already registered in the current thread, and the failure lands on a test that does not mock anything. What causes this, and how do you prevent it?

level: seniorimportance: must knowfreq 44%

basics

~20 s

A MockedStatic scope was opened and never closed, so the thread-local registration survives into later tests on the same thread. Any attempt to register the same type again fails, and unrelated tests see mocked statics. Always open the scope in try-with-resources, or close it in teardown.

open as a page

A Mockito deep-stub chain still fails: one call in the middle returns null, and another throws ClassCastException on a method declared to return a generic type variable T. What are the limits of deep stubbing that explain both?

level: middleimportance: should knowfreq 22%

basics

~20 s

A deep stub only creates a child mock when the return type is mockable; otherwise it falls back to the plain default value, so String returns null and int returns 0, ending the chain. For a method returning a type variable, erasure leaves only the bound (often Object), so the child mock is of the wrong type and the cast fails.

open as a page

Mocking a fluent builder in Mockito usually breaks with a NullPointerException on the second chained call. Why, and how does the Answers.RETURNS_SELF default answer fix it?

level: middleimportance: should knowfreq 30%

basics

~20 s

Each unstubbed builder method returns null by default, so the next call in the chain dereferences null. RETURNS_SELF makes an unstubbed method return the mock itself whenever the return type is assignable from the mock's type, keeping the chain alive.

open as a page

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%

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.

open as a page

How do you select which mock maker Mockito uses, and what happened to the mockito-inline artifact people used to add to their test dependencies?

level: middleimportance: should knowfreq 42%

basics

~20 s

Since Mockito 5 the inline maker is the default in mockito-core, so nothing is needed. Otherwise put a file mockito-extensions/org.mockito.plugins.MockMaker on the test classpath containing mock-maker-inline. The mockito-inline artifact existed only to flip that default and is retired; mockito-subclass now provides the old behaviour.

open as a page

Using Mockito's `mockConstruction`, the object your code creates internally comes back as an unstubbed mock, so the very next call returns null and blows up. How do you give each constructed instance behaviour, and how do you get hold of the instances afterwards to assert on them?

level: middleimportance: should knowfreq 33%

basics

~20 s

Pass a MockInitializer as the second argument: mockConstruction(Foo.class, (mock, context) -> when(mock.get()).thenReturn("x")). It runs right after each construction, so the mock is stubbed before the code uses it. Afterwards read scope.constructed() — the ordered list of every instance created.

open as a page

You need to prove that your code invoked a particular static factory method exactly once with a given argument, and never invoked another static on that same class. How is that verification expressed with Mockito's static mocking, and how does it differ from verifying an ordinary mock?

level: middleimportance: should knowfreq 32%

basics

~20 s

Verification goes through the MockedStatic handle: mocked.verify(() -> Factory.create("id")) with an optional mode such as times(2) or never(), plus mocked.verifyNoMoreInteractions(). The invocation is described by a lambda instead of by calling a method on a mock instance.

open as a page

With a Mockito mock created using RETURNS_DEEP_STUBS, how do you verify an interaction on the last object of a call chain, and why are invocation counts on the intermediate objects untrustworthy?

level: seniorimportance: should knowfreq 20%

basics

~20 s

Verify on the leaf: verify(config.getServer()).restart() works because getServer() returns the cached child mock. But every call to getServer() — from production code, from your stubbing, and from inside the verify argument — is recorded on the parent, so times(n) on chain links and verifyNoMoreInteractions are misleading.

open as a page

What does Mockito's Answers.CALLS_REAL_METHODS do to a mock, and how does the resulting object differ from one created with Mockito.spy(existingInstance)?

level: seniorimportance: should knowfreq 38%

basics

~20 s

CALLS_REAL_METHODS makes unstubbed calls run the real implementation on a mock instance created without running any constructor — so its fields are null/zero. spy(instance) wraps an object you constructed, so its state is real. Both need doReturn/doThrow to stub safely.

open as a page

How do you give a Mockito mock a default answer of your own rather than one of the built-in Answers constants, and what kinds of behaviour justify writing one?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Pass an Answer implementation where an Answers constant would go: mock(Type.class, invocation -> ...) or withSettings().defaultAnswer(myAnswer). Inside, InvocationOnMock gives you the method, arguments, the mock and callRealMethod(). Justified for argument-dependent returns, failing loudly on unstubbed calls, or delegating to a fake.

open as a page

Running a Mockito 5 test suite on JDK 21 or newer prints a warning that a Java agent has been loaded dynamically and that this will be disallowed by default in a future release. Where does that come from and how do you resolve it?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Mockito's inline mock maker attaches ByteBuddy's agent to the running JVM at startup. JDK 21 warns about dynamic agent attachment because it is being phased out. Fix it by starting tests with -javaagent pointing at the byte-buddy-agent jar, or silence it with -XX:+EnableDynamicAgentLoading.

open as a page

A team keeps adding Mockito constructor-interception scopes to unit-test classes that instantiate their collaborators with `new` internally. How would you decide between keeping that approach and refactoring the code to inject the collaborator instead?

level: principalimportance: should knowfreq 28%

basics

~20 s

Interception is a workaround for code you cannot change: third-party classes, legacy, or a risky refactor. For your own code, injecting the collaborator (constructor parameter or a small factory interface) removes the bytecode magic, makes the dependency visible in the API, and keeps tests readable. Prefer the refactor; keep interception at the edges.

open as a page

A codebase has accumulated dozens of tests that mock static methods with Mockito. As the technical lead, how would you decide where that is acceptable and where the code should change instead?

level: principalimportance: should knowfreq 29%

basics

~20 s

Allow it at boundaries you do not own — third-party or JDK statics, legacy code you cannot change. Discourage it in your own domain code, where injecting a Clock, an ID generator, or a thin adapter interface removes the need entirely, keeps tests fast and thread-safe, and makes the dependency visible in the API.

open as a page

A large Java test suite slows down and its heap usage climbs steadily after Mockito's instrumentation-based mock maker becomes the default. What costs does that mock maker introduce, and what would you do about them?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

Instrumenting each newly mocked class costs time on first use, and Mockito keeps per-mock state including recorded invocations and their arguments, so long-running suites accumulate memory. Clear it with Mockito.framework().clearInlineMocks() after each test, null out fields, and use the subclass maker for hot simple types.

open as a page

Mockito offers `mockConstructionWithAnswer(Class, Answer, Answer...)` alongside the initializer-based `mockConstruction`. What does that variant actually do with the answers you pass, and where does it fit?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

It intercepts construction like mockConstruction, but instead of a stubbing callback you supply default Answers: the first Answer backs the first constructed instance, each further Answer the next construction, and the last one is reused for all remaining instances. It applies to every method, not a chosen one.

open as a page

Mockito's own documentation warns that RETURNS_DEEP_STUBS usually means something is wrong with the design. What is the underlying argument, when would you accept deep stubs anyway, and what would you reach for first instead?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

A deep stub encodes a call chain, so the test depends on the shape of an object graph rather than on behaviour — refactoring the intermediate types breaks tests that never changed meaning. Accept it for third-party fluent APIs and legacy characterization; otherwise inject the leaf collaborator, use real value objects, or hide the chain behind an adapter you own.

open as a page

Mockito's Answers.RETURNS_MOCKS makes an unstubbed call return a fresh mock of its return type instead of null. Would you adopt that as the default for a whole test suite? Argue the tradeoffs.

level: principalimportance: nice to knowfreq 20%

basics

~20 s

Rarely worth it suite-wide. It removes NPE noise but makes tests pass while exercising nothing real: every collaborator silently produces an object, so missing stubs and broken call chains stop failing. Prefer explicit stubbing plus strict stubs, and use it locally for wide legacy interfaces.

open as a page

You own a Java codebase whose tests must also run in environments where bytecode instrumentation is unavailable or restricted. How do you decide how far to let the test suite depend on Mockito's instrumentation-based mocking?

level: principalimportance: nice to knowfreq 16%

basics

~20 s

Treat instrumentation-only mocking as a capability with a cost. Keep the inline maker as the default, but confine tests that need final-type, static or constructor mocking to a tagged subset, and make sure the core suite runs green under the subclass maker so restricted environments stay viable.

open as a page