A test that uses Mockito's @InjectMocks fails with a NullPointerException because one dependency inside the object under test is null, even though the test class declares a matching @Mock field for it. How do you diagnose and fix that?
answer
- Injection never reports failure — expect a null, not an error
- All fields null = annotations never processed
- Wide constructor wins → setter/field route never runs
- final/static fields are skipped
- Same type → name matching; constructor args are type-only
basics
~20 sInjection failures are silent, so work through the causes: the wide constructor won and setter/field injection never ran; the field is final or static; two dependencies share a type and names do not match; or annotations were never processed. The reliable fix is to construct the object explicitly in the test.
solid answer
~60 sMockito never reports a failed injection, so a null dependency is always "which route did it take, and why did that route skip this field?" Check in this order: 1. **Was anything processed at all?** No `MockitoExtension`/`openMocks`/runner means *every* field is null, not just one. 2. **Which strategy ran?** If the class has a constructor with parameters, constructor injection wins and setter/field injection never happens. A dependency that is not a constructor parameter stays null. 3. **Is the field `final` or `static`?** Field injection deliberately skips both. 4. **Type ambiguity.** Two dependencies of the same type are separated only by name matching (and constructor args are matched by type alone), so one can end up null or swapped. 5. **Wrong pool.** Only `@Mock`/`@Spy` fields are candidates — an object you built in `@BeforeEach` is invisible. The durable fix is usually to stop relying on the heuristic: give the class one all-args constructor and call it directly in the test, so the next signature change fails at compile time instead of at runtime.
code
java · 26 linesclass OrderService {
private final Repo repo;
private final Auditor auditor; // added later to the constructor
OrderService(Repo repo, Auditor auditor) {
this.repo = repo;
this.auditor = auditor;
}
}
// Fails silently if the test class forgot @Mock Auditor auditor;
// -> auditor is null, NPE inside placeOrder(...)
// Fix that cannot rot:
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock Repo repo;
@Mock Auditor auditor;
OrderService service;
@BeforeEach
void setUp() {
service = new OrderService(repo, auditor); // compile error if signature changes
}
}go deeper
Know the first check — that missing MockitoExtension or openMocks leaves everything null — and that Mockito does not warn about injection.
Walk the checklist: which strategy ran, is the field final/static, is the type ambiguous, is the mock actually in the pool.
Diagnose from the class shape rather than by trial and error, and land on the durable fix — one all-args constructor plus explicit construction in the test.
Name the systemic risk: a fail-open reflective wiring mechanism means constructor changes rot tests silently across the suite; set a convention that makes wiring a compile-time concern.
## Why the failure is silent Mockito's documented behaviour is that it will not report a failure to inject. There is no strict mode for wiring. What you get instead is a `NullPointerException` thrown from inside production code, several frames below the test, which makes people suspect the production class rather than the test setup. Recognising the smell — "a collaborator is null in a test that has a mock for it" — is most of the diagnosis. ## The checklist **1. Nothing processed the annotations.** If *all* the fields are null, the cause is missing `@ExtendWith(MockitoExtension.class)`, a missing `MockitoAnnotations.openMocks(this)` in `@BeforeEach`, or an inherited base test class whose `@BeforeEach` was overridden without calling `super`. Distinguish this case first: it is a different bug from selective injection failure. **2. The constructor strategy won and excluded the field.** Injection is a chain — constructor, then setter, then field — and the first success ends it. If the class has any constructor with parameters, Mockito uses the biggest one and never touches setters or fields afterwards. So a dependency introduced as a plain field with a setter, on a class that also has a wide constructor, is structurally unreachable. Symptom: exactly one dependency null, and it happens to be the one that is not a constructor parameter. **3. The constructor argument had no candidate.** Mockito resolves each parameter by type and passes `null` for anything unmatched. Common triggers: the parameter type is a JDK type nobody mocked (`Clock`, `String`, `Duration`); the mock is declared as a subtype that is not assignable to the parameter type; or the parameter is a generic type whose erasure does not match. The mock field exists, but not for the type the constructor actually asks for — check the declared types character by character rather than by memory. **4. `final` or `static` field.** Field injection skips both. A `private final Auditor auditor;` that is *not* assigned by the constructor Mockito chose has no path to a value. This also explains the confusing case where making the field non-final "fixes" it. **5. Same-type ambiguity.** With two `DataSource` dependencies, type filtering leaves two candidates and Mockito falls back to comparing the mock field's name with the target property name. Rename `@Mock DataSource ds1` to match the target field exactly, or stop using the annotation for that class. For constructor parameters the situation is worse — argument resolution is type-driven, so two same-type parameters can be filled in the wrong order with no diagnostic at all. **6. The candidate is not in the pool.** Only `@Mock` and `@Spy` fields of the test class (including inherited ones) are injection candidates. A collaborator you built by hand in `@BeforeEach`, a `@Captor`, or a static helper is not. **7. You reassigned the field.** Assigning `service = new OrderService(...)` in `@BeforeEach` after the extension has already injected — or before, in the wrong order — leaves you looking at a different instance from the one Mockito wired. Similarly, mutating a mock field after injection does not retroactively change what was injected: the object holds the reference it was given, so replacing the *field* in the test class has no effect on the object under test. ## How to confirm quickly Put a breakpoint or a temporary assertion at the top of the test and inspect the object under test. Reflection on the field, or simply stepping in, tells you which dependencies arrived. Then compare against the class's constructors: if it has a wide constructor, the answer is almost always cause 2 or 3. ## The fixes, in order of preference - **Make the dependency a constructor parameter.** One all-args constructor removes the ambiguity entirely: everything arrives through the single obvious route, and adding a parameter breaks the test's compilation if the test builds the object by hand. - **Construct the object explicitly in the test.** `service = new OrderService(repo, mailer, auditor);` in `@BeforeEach`. This costs one line and converts a runtime NPE into a compile error next time the signature changes. For classes with mixed literal and mockable arguments, this is the only sane option. - **Pre-initialize the `@InjectMocks` field** if you want to keep the annotation but skip constructor injection: `@InjectMocks OrderService service = new OrderService("url", 3000);` — Mockito then only runs setter/field injection on your instance. - **Rename mocks to match target fields** as a targeted fix for same-type ambiguity. ## The systemic lesson This failure mode is not really about Mockito; it is about a wiring mechanism that resolves by reflection and fails open. In a large suite, the cost shows up when someone adds a constructor parameter: nothing fails to compile, tests keep running, and a null appears in whichever path exercises the new dependency. Teams that have been burned by it tend to standardise on explicit construction for exactly that reason.
- How would you tell "annotations were never processed" apart from "one dependency failed to inject"?Look at how many things are null. Missing MockitoExtension, runner, or openMocks leaves every annotated field null, including the @InjectMocks field itself, and the NPE usually fires on the very first line of the test. A selective injection failure leaves the object under test constructed and only one collaborator null, with the NPE thrown from inside production code.
- Someone adds a parameter to the constructor of the class under test. Why do the @InjectMocks-based tests keep compiling but start failing?Because the test never names the constructor. Mockito picks the biggest constructor at runtime and passes null for any parameter with no matching @Mock field, so the compiler sees nothing wrong. The failure appears as an NPE the first time the new dependency is used. Constructing the object explicitly in the test would have turned this into a compile error.
- Would Mockito's strict stubbing settings have caught this?No. Strictness governs stubbing — unused stubs and argument mismatches — not dependency injection. Wiring failures are outside its scope, which is precisely why this class of bug survives in otherwise strict suites.
saying these in an interview costs you the question
- Assuming Mockito throws when it cannot satisfy a dependency
- Blaming the production class for the NPE instead of inspecting the injection route
- Adding a setter to "fix" it on a class whose wide constructor already won the chain
- Thinking reassigning the @Mock field in the test changes what the already-injected object holds
- Expecting strict stubbing to catch injection problems