Skip to content
Latchkey

Spring Test "@MockBean state leaks between tests" - Fix in CI

A @MockBean is reset by Spring only when the test framework resets it around each method; if the cached application context is shared and reset is bypassed, stubs and verified interactions from an earlier test bleed into a later one and cause order-dependent failures.

What this error means

A test passes alone but fails when run after another that stubbed the same @MockBean, or a verify(...) counts interactions from a previous test. Failures are non-deterministic and depend on execution order.

junit
org.mockito.exceptions.verification.TooManyActualInvocations:
paymentClient.charge(<any>);
Wanted 1 time:
But was 2 times:
  -> at com.example.PaymentTest.charges(PaymentTest.java:41)
  -> at com.example.RefundTest.refunds(RefundTest.java:33)  // leaked from prior test

Common causes

Context reuse without per-method reset

Spring caches the context across test classes. @MockBean is reset by MockitoTestExecutionListener (BEFORE/AFTER each method) - if that listener is disabled or overridden, stubs persist.

Manual mocks instead of @MockBean

A hand-rolled mock() placed in the context as a @Bean is never reset by Spring, so its state accumulates across the whole suite.

Stubbing in a static/shared field

Stubs stored on a static field, or set in @BeforeAll, survive every test in the class regardless of mock reset.

How to fix it

Use @MockBean, not a manual @Bean mock

Let Spring manage the mock so its MockReset.AFTER semantics clear it around each test method.

java
@SpringBootTest
class PaymentTest {
  @MockBean PaymentClient paymentClient; // auto-reset per method
}

Re-stub per test, not in @BeforeAll

Set up stubbing in @BeforeEach so each method starts from a clean mock.

java
@BeforeEach
void stub() {
  when(paymentClient.charge(any())).thenReturn(Receipt.ok());
}

Do not disable the Mockito listener

Ensure default test execution listeners remain active; merging custom listeners must keep MockitoTestExecutionListener.

java
// If you set listeners explicitly, MERGE rather than replace
@TestExecutionListeners(
  listeners = MyListener.class,
  mergeMode = MergeMode.MERGE_WITH_DEFAULTS)

How to prevent it

  • Always use @MockBean/@SpyBean rather than placing manual mocks in the context.
  • Stub in @BeforeEach; avoid static fields and @BeforeAll for mutable mock state.
  • Keep default TestExecutionListeners so per-method mock reset runs.

Frequently asked questions

What causes ""@MockBean leaks between tests""?
Spring caches the context across test classes. @MockBean is reset by MockitoTestExecutionListener (BEFORE/AFTER each method) - if that listener is disabled or overridden, stubs persist.
How do I fix "@MockBean leaks between tests"?
Let Spring manage the mock so its MockReset.AFTER semantics clear it around each test method.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card