skip to content

Mocking Static Methods

mockStatic returns a scoped MockedStatic that must be closed, ideally in try-with-resources, and it is thread-local by design. Interviewers usually add that needing to mock a static is a hint the design has a hidden dependency.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

Why should a MockedStatic be used inside a try-with-resources block, and what goes wrong if you don't close it?

level: seniorimportance: must knowfreq 60%

answer

  1. MockedStatic is AutoCloseable
  2. try-with-resources closes even on throw
  3. unclosed mock leaks to next test on same thread
  4. order-dependent flakiness
  5. double-mock-same-type error if not closed

basics

~20 s

MockedStatic is AutoCloseable. Opening it in try-with-resources guarantees it closes at the end of the test. If you forget to close it, the static stays mocked and leaks into later tests on the same thread, causing confusing failures.

solid answer

~50 s

A MockedStatic registers a global interceptor on the class for the current thread and stays active until close(). Try-with-resources guarantees close() runs even if the test body throws an exception, deterministically tearing the mock down at the end of the scope. If you forget to close it — or close it only on the happy path — the interceptor leaks: subsequent tests running on the same thread still see the mocked static instead of the real one, producing order-dependent, flaky failures that are hard to trace back to the source. Mockito even fails fast if you try to mock the same static type twice without closing the first scope. Keep the try block tight: open the MockedStatic, stub, exercise, verify, and let the block close it, so the real static is restored before the next test runs.

code

java · 13 lines
java
@Test
void usesFixedClockInstant() {
    try (MockedStatic<Instant> mocked = Mockito.mockStatic(Instant.class)) {
        Instant fixed = Instant.parse("2026-01-01T00:00:00Z");
        mocked.when(Instant::now).thenReturn(fixed);

        // code under test calls Instant.now() and gets `fixed`
        assertEquals(fixed, service.timestamp());

        mocked.verify(Instant::now);
    }
    // mock closed here; Instant.now() is real again for the next test
}

go deeper

for a junior

Knows to wrap mockStatic in try-with-resources because the docs/examples do, even if unsure why.

for a middle

Explains that the mock must be closed and that try-with-resources does it automatically, including on exceptions.

for a senior

Articulates the thread-reuse leak, order-dependent flakiness, the double-registration error, and exception-safety of the implicit finally.

for a principal

Sets team conventions (no MockedStatic fields without exception-safe teardown), explains why leaks produce blame-the-wrong-test flakiness, and reviews for it.

## Recap: what MockedStatic holds `Mockito.mockStatic(Foo.class)` installs a **process-level interceptor** on `Foo`'s static methods, scoped to the **current thread**, and hands you a `MockedStatic<Foo>` handle. This handle is **stateful and live** — the real static method is shadowed for as long as the handle is open. ## What AutoCloseable / try-with-resources is `AutoCloseable` is the Java interface whose `close()` is called automatically by a **try-with-resources** statement: ```java try (MockedStatic<Foo> mocked = Mockito.mockStatic(Foo.class)) { // mocked is live here } // close() has already run here — mock is gone ``` The compiler emits a `finally` that calls `close()` **whether the block completes normally or throws**. This is the same guarantee you rely on for files and JDBC connections. ## Why it matters specifically for static mocks 1. **Leak across tests.** A test runner reuses threads. If a test opens a MockedStatic and never closes it, the next test *on that same thread* inherits the live mock. Now `Foo.now()` returns the previous test's stubbed value instead of the real one. The failing test did nothing wrong — the *previous* test leaked. These are classic **order-dependent / flaky** failures. 2. **Exception safety.** If you call `mockStatic` and then `mocked.close()` manually at the end of the method, an assertion failure or exception *before* that line skips the close — exactly when you most need it. Try-with-resources runs close in the implicit `finally`, so it survives the throw. 3. **Double-registration guard.** Mockito refuses to mock the same static type a second time while a scope is still open, throwing an error about a previous registration. Forgetting to close turns the *next* test's legitimate `mockStatic(Foo.class)` into a hard error. ## The disciplined pattern - Open the MockedStatic inside `try (...)`. - Stub only what you need. - Exercise and assert inside the block. - Never store a MockedStatic in a field that outlives a single test without an explicit, exception-safe teardown. If you genuinely need a per-test field (e.g. set up in `@BeforeEach`), you must close it in `@AfterEach` in a `finally`-equivalent way — but try-with-resources is preferred because it cannot be forgotten. ## Why try-with-resources beats manual close Manual `close()` is correct *only* if you also wrap the body in try/finally yourself — which is exactly what try-with-resources does for you, with less code and no chance of skipping it on the error path.

  • A teammate sets up mockStatic in @BeforeEach but never closes it. What symptom appears and why?
    Later tests on the same thread inherit the live mock, so unrelated tests fail or Mockito throws 'static mocking is already registered'. The failures are order-dependent because they depend on which test leaked first.
  • Is manual mocked.close() ever acceptable?
    Yes, but only inside a try/finally (e.g. @AfterEach) so it runs on the exception path too. Try-with-resources is preferred because it gives that guarantee for free and cannot be forgotten.

saying these in an interview costs you the question

  • Closing the MockedStatic manually on the last line without try/finally — skipped when the body throws.
  • Storing a MockedStatic in a long-lived field and never tearing it down.
  • Blaming a flaky test on the test that fails rather than the earlier test that leaked the mock.

context

open as a page

How do you mock a static method with Mockito, and what does Mockito.mockStatic return?

level: middleimportance: should knowfreq 55%

basics

~10 s

Call Mockito.mockStatic(SomeClass.class). It returns a MockedStatic handle you use to stub the static methods. While that handle is open, real static calls are replaced by your stubs; closing it restores the real behavior.

open as a page

What is the inline mock-maker, why is it required to mock statics, and how do its versions/configuration differ across Mockito releases?

level: middleimportance: should knowfreq 40%

basics

~20 s

The inline mock-maker is Mockito's engine that rewrites bytecode at runtime so it can intercept static (and final) methods. mockStatic only works with it. It's the default in Mockito 5+; in older versions you added the mockito-inline dependency.

open as a page

What is the thread-local scope of a MockedStatic, and how does it affect concurrent or async code under test?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A MockedStatic only intercepts static calls made on the same thread that created it. Code under test that runs the static call on a different thread (a thread pool, an async task) sees the real method, not your stub.

open as a page

Why is frequently needing to mock static methods often considered a design smell, and what refactorings remove the need?

level: principalimportance: should knowfreq 45%

basics

~20 s

Reaching for static mocks usually means your code calls hard-coded global things (clocks, randomness, static singletons) it can't swap out for tests. Injecting those as parameters or interfaces makes the code testable with plain mocks and removes the need.

open as a page