In a JUnit 5 test that uses Mockito's @MockitoSettings annotation, how do you change the strictness for a test class, what scope does that choice have, and how does it interact with per-mock leniency?
answer
- @MockitoSettings implies @ExtendWith(MockitoExtension.class)
- innermost declaration wins: method > nested > class
- affects every @Mock/@Spy in scope
- narrow ladder: lenient() stub < lenient mock < class LENIENT
- leniency only relaxes, never tightens
basics
~20 sPut @MockitoSettings(strictness = Strictness.LENIENT) on the test class; it implies @ExtendWith(MockitoExtension.class), so you do not need both. It sets the level for every mock in that class. Prefer narrower tools first: @Mock(lenient = true) or lenient() on a single stubbing.
solid answer
~50 s`@MockitoSettings` (package `org.mockito.junit.jupiter`) is the JUnit 5 knob for the extension's strictness: ```java @MockitoSettings(strictness = Strictness.LENIENT) class LegacyServiceTest { @Mock Repo repo; } ``` It is itself meta-annotated with `@ExtendWith(MockitoExtension.class)`, so adding both is redundant. The extension resolves it from the current extension context and walks outward, so it can sit on the test class, on a `@Nested` class, or on an individual test method, and the innermost declaration wins. Scope is *every mock in that scope* — which is why it is the bluntest of the three tools. The ladder, narrowest first: 1. `lenient().when(mock.foo()).thenReturn(x)` — one stubbing. 2. `@Mock(lenient = true)` / `withSettings().lenient()` — one mock. 3. `@MockitoSettings(strictness = LENIENT)` — the whole class. A class-wide LENIENT turns off unused-stubbing *and* argument-mismatch detection for everything, so it should be a temporary migration marker with a comment, not a default.
code
java · 18 lines// Blunt: every mock in the class loses both hygiene checks
@MockitoSettings(strictness = Strictness.LENIENT)
class LegacyBillingTest {
@Mock Clock clock;
}
// Preferred: stay strict, relax exactly what needs it
@ExtendWith(MockitoExtension.class)
class BillingTest {
@Mock(lenient = true) Clock clock; // shared fixture stub, not every test uses it
@Mock InvoiceRepository invoices; // still strict
@BeforeEach
void setUp() {
when(clock.instant()).thenReturn(Instant.EPOCH);
lenient().when(invoices.count()).thenReturn(0L); // one stubbing only
}
}go deeper
Know the annotation, where it goes, and that it defaults to STRICT_STUBS.
Explain that it implies the extension, that inner declarations win, and that per-mock and per-stub leniency are the narrower tools.
Argue the policy: strict by default, relax at the narrowest scope, treat WARN and class LENIENT as tracked temporary states.
Discuss how strictness exceptions are governed at scale — whether a base class or composed annotation is acceptable, and what signal a spreading LENIENT gives about fixture design.
## The annotation `org.mockito.junit.jupiter.MockitoSettings` is the JUnit 5 configuration point for the Mockito extension. Its single attribute today is `strictness`, defaulting to `Strictness.STRICT_STUBS`: ```java @MockitoSettings(strictness = Strictness.WARN) class PaymentServiceTest { ... } ``` Because `@MockitoSettings` is meta-annotated with `@ExtendWith(MockitoExtension.class)`, writing both annotations on the same class is harmless but redundant — the annotation alone already registers the extension, and forgetting that is a common review comment. ## Resolution and scope The extension looks up `@MockitoSettings` from the current JUnit extension context and walks outward through enclosing contexts, so the declaration can live on: - the test class (the usual place), - an inner `@Nested` class, which overrides the outer class's choice for the tests inside it, - an individual `@Test` method, which overrides the class-level choice for that test only. The innermost declaration wins. Whatever level is resolved is applied to the Mockito session the extension opens *before each test method* and closes after it, so the checks are per-test-method regardless of where the annotation sits. It affects **every mock created by that extension in scope**: `@Mock` fields, `@Spy` fields, and mock parameters injected into the test method or constructor. It does not reach mocks created some other way inside the test body, nor mocks managed by another framework. ## Interaction with per-mock and per-stub leniency There are three levers, and they compose from broad to narrow: | Lever | Scope | |---|---| | `@MockitoSettings(strictness = ...)` | every mock in the class/nested class/method | | `@Mock(lenient = true)`, `Mockito.mock(Foo.class, withSettings().lenient())` | one mock | | `lenient().when(...)`, `doReturn(x).lenient().when(mock).foo()` | one stubbing | Leniency only ever *relaxes*. Declaring `@Mock(lenient = true)` inside a class with the default STRICT_STUBS excludes that mock's stubbings from both hygiene checks while the rest of the class stays strict. The reverse does not exist: you cannot mark a single stubbing "strict" inside a class that is globally LENIENT, because the session-level checks that would report it are simply not running. That asymmetry is the practical argument for keeping the class strict and relaxing narrowly. ## When each level is the right call - **STRICT_STUBS (default)** — the normal choice. Leave it alone. - **WARN** — a deliberate, temporary state while migrating a legacy class: the console tells you what to clean up without a red build. Track it, because WARN in a passing build is invisible. - **LENIENT** — justified for a small set of shapes: a test class with a heavyweight shared fixture that stubs a collaborator's whole surface for a family of scenarios, or a data-driven/parameterized class where each parameter set exercises a different subset of stubbings. Even then, prefer marking the specific fixture mock lenient over the whole class, and leave a comment saying why. A class-wide LENIENT that outlives its migration is a real cost: the class silently loses argument-mismatch diagnostics, so a future wrong-argument bug shows up as a `NullPointerException` in unrelated code. ## Common mistakes 1. Adding `@MockitoSettings` and expecting it to affect mocks created by `Mockito.mock(...)` inside a test method body — it does, only if those mocks are created while the extension's session is open *and* the session tracks them; mocks created ad hoc inside the test body are registered with the current session, but mocks created in a static initialiser or shared across classes are not. Keep mocks as `@Mock` fields to avoid the ambiguity. 2. Using LENIENT to silence one noisy stubbing, when `lenient()` on that stubbing was the intended fix. 3. Assuming `@MockitoSettings` can be placed once at the module or package level — it is a JUnit annotation resolved through the extension context, not a global configuration file. For suite-wide defaults you would need a custom composed annotation or a base class, which is itself a smell worth questioning. ## Interview framing Show the annotation, state that it implies the extension, and then immediately move to the judgment: strictness should be relaxed at the narrowest scope that solves the problem, and a whole-class LENIENT is an admission of a fixture problem you are choosing not to fix today.
- Do you still need @ExtendWith(MockitoExtension.class) when you use @MockitoSettings?No. @MockitoSettings is meta-annotated with @ExtendWith(MockitoExtension.class), so the annotation alone registers the extension. Adding both is not an error, just redundancy that reviewers usually remove.
- Can you make one mock strict inside a class annotated LENIENT?No. Leniency only relaxes; there is no per-mock or per-stub way to opt back into strict stubbing once the session-level strictness is LENIENT, because the checks that report unused stubbings and argument mismatches are not running for that session at all. That asymmetry is the reason to keep the class strict and relax narrowly instead.
saying these in an interview costs you the question
- Thinking @MockitoSettings needs @ExtendWith(MockitoExtension.class) alongside it.
- Believing you can raise a single mock back to strict inside a LENIENT class.
- Reaching for class-wide LENIENT to silence one stubbing.
- Assuming @MockitoSettings is a global/module-level configuration rather than a per-context JUnit annotation.
- Expecting it to affect Spring-managed mocks or mocks another framework created.