Mockito exposes a MockitoSession API via Mockito.mockitoSession(). What is it for, how do you drive one by hand, and what breaks if you forget to finish it?
answer
- session = the primitive under runner/rule/extension
- mockitoSession().initMocks(this).strictness(...).startMocking()
- finishMocking() mandatory — in finally / after-each
- finishMocking(failure) keeps the real failure on top
- sessions default to STRICT_STUBS
basics
~20 sMockitoSession is the mechanism behind the JUnit runner, rule and extension: it initialises mocks, applies a strictness level, and validates stubbing hygiene when finished. You build it with Mockito.mockitoSession().initMocks(this).strictness(...).startMocking() and must call finishMocking() afterwards, or the checks never run and mock state leaks into the next test.
solid answer
~50 s`MockitoSession` is the underlying API that the JUnit 4 runner/rule and the JUnit 5 extension are thin wrappers over. You reach for it directly when no such integration exists — TestNG, Spock, a custom harness, or a base class that has to manage mocks itself. ```java MockitoSession session = Mockito.mockitoSession() .initMocks(this) .strictness(Strictness.STRICT_STUBS) .startMocking(); try { // test body } finally { session.finishMocking(); } ``` `startMocking()` initialises `@Mock`/`@Spy` fields and installs the strictness listener; `finishMocking()` runs the validation (unused stubbings, misuse detection) and tears down the thread-local state. If you skip `finishMocking()`, you lose the hygiene checks entirely *and* leave the session open, which produces confusing "unfinished session" behaviour and leaked mock state in later tests. There is also `finishMocking(Throwable failure)`: pass the test's own failure so Mockito adds stubbing hints rather than masking the real cause with an `UnnecessaryStubbingException`.
code
java · 13 linesMockitoSession session = Mockito.mockitoSession()
.initMocks(this)
.strictness(Strictness.STRICT_STUBS)
.startMocking();
Throwable failure = null;
try {
runTestBody();
} catch (Throwable t) {
failure = t;
throw t;
} finally {
session.finishMocking(failure); // hints instead of masking the real failure
}go deeper
Recognise the API and that it exists for non-JUnit harnesses; you are unlikely to write one.
Be able to write the start/finish pair correctly, including finishing in a finally block.
Explain that runner/rule/extension are wrappers over a session, why finishMocking(Throwable) matters, and the leak/cross-test-failure symptoms of forgetting it.
Use it to explain where strictness can and cannot be enforced across a heterogeneous test estate, and decide whether a custom base class owning sessions is worth the maintenance.
## What it is `MockitoSession` (`org.mockito.MockitoSession`, built via `Mockito.mockitoSession()`) is the primitive that all Mockito test integrations are built on. A session: 1. initialises annotated fields (`@Mock`, `@Spy`, `@Captor`, `@InjectMocks`) on the object you pass to `initMocks`, 2. installs a listener that enforces the chosen `Strictness`, 3. on completion, runs the validation that produces `UnnecessaryStubbingException` and Mockito's misuse diagnostics, 4. cleans up the thread-local state Mockito keeps between the two points. The JUnit 4 `MockitoJUnitRunner`, the `MockitoRule`, and the JUnit 5 `MockitoExtension` all do exactly this around each test; the session API is what you use when there is no such integration. ## Driving one by hand ```java public class TestNgOrderServiceTest { @Mock OrderRepository repo; private MockitoSession session; @BeforeMethod public void before() { session = Mockito.mockitoSession() .initMocks(this) .strictness(Strictness.STRICT_STUBS) .name(getClass().getSimpleName()) .startMocking(); } @AfterMethod public void after() { session.finishMocking(); } } ``` Builder options worth knowing: - `initMocks(Object...)` — one or more objects whose annotated fields should be initialised (useful when a base class also holds mocks). - `strictness(Strictness)` — the level; **STRICT_STUBS is the default for a session**, which is why hand-rolled sessions are the strict option by construction. - `name(String)` — a label that shows up in hint messages. - `logger(MockitoSessionLogger)` — redirect WARN-level hints somewhere other than stdout. `startMocking()` returns the `MockitoSession`; `finishMocking()` is mandatory and belongs in a `finally` or the framework's after-each hook so it runs even when the test fails. ## finishMocking(Throwable) The overload `finishMocking(Throwable failure)` exists for a specific ordering problem. Suppose a test fails on a genuine assertion, and it *also* left an unused stubbing behind. If you call the no-arg version, Mockito reports `UnnecessaryStubbingException`, which can replace or obscure the real failure — the developer chases a stubbing that was never the point. Passing the actual failure lets Mockito see that the test already failed, so instead of throwing its own exception it *decorates* the report with stubbing hints ("this stubbing was never used — it may explain the failure"). Test frameworks that integrate Mockito properly use this overload; hand-rolled integrations usually should too. ## What breaks if you forget finishMocking() - **The hygiene checks never run.** Unused stubbings and misuse go unreported, so a session you thought was STRICT_STUBS behaves like LENIENT for the end-of-test checks. (Argument-mismatch detection still fires during the test, because that listener is already installed.) - **State leaks.** Mockito keeps thread-local state for the open session; a subsequent test starting a new session can hit "unfinished session" style problems, and inline-mock-maker mock references stay reachable longer than necessary, which shows up as memory pressure in large suites. - **Failures land in the wrong test.** Anything Mockito would have reported at the end of test A can surface during test B, producing the classic "only fails when the whole class runs" report. The same discipline applies to `MockitoAnnotations.openMocks(this)`, which returns an `AutoCloseable` you are expected to close — though note that `openMocks` only initialises fields, it does not give you strictness. If you want hygiene checks without a JUnit integration, you want a session, not `openMocks`. ## When to actually use it In a JUnit 5 codebase, essentially never — the extension is strictly better and less code. Legitimate cases: - non-JUnit runners (TestNG, Spock, Cucumber steps with their own lifecycle), - a custom test base class or framework where you own the before/after hooks, - a test that needs two differently-configured mock scopes inside one method, though that usually means the test is doing too much, - teaching or debugging: reading the session API makes clear that strictness is a lifecycle concern, not a property of the mock object. In an interview, the useful signal is exactly that last point: the runner, rule and extension are conveniences over a session, so if you understand the session you can explain why bare `Mockito.mock(...)` can never enforce strict stubbing.
- Why does finishMocking(Throwable) exist instead of just finishMocking()?So Mockito can tell the difference between a test that passed with a dirty stubbing and a test that already failed for another reason. When you hand it the real failure, Mockito attaches stubbing hints to the report rather than throwing UnnecessaryStubbingException on top, which would hide the assertion that actually broke.
saying these in an interview costs you the question
- Saying MockitoSession is an alternative to mocks rather than the lifecycle that manages them.
- Believing MockitoAnnotations.openMocks(this) gives you strictness checks.
- Calling finishMocking() only on the success path, so failing tests skip validation and leak state.
- Assuming a session defaults to LENIENT — it defaults to STRICT_STUBS.
- Recommending hand-rolled sessions in a JUnit 5 codebase where the extension already does the job.