What is @MockBean, and what is the hidden performance trade-off of using it heavily across @SpringBootTest classes?
answer
- @MockBean = Mockito mock inserted into the context, replaces real bean
- implemented as ContextCustomizer => part of cache key
- different mock sets => different cached contexts
- consolidate mocks in base class; prefer plain Mockito unit tests
- @MockBean deprecated -> @MockitoBean, same caching
basics
~20 s@MockBean adds (or replaces) a bean in the test context with a Mockito mock. The hidden cost: each distinct set of @MockBean declarations is part of the context cache key, so heavy/varied use forks many separate contexts and slows the suite.
solid answer
~50 s`@MockBean` (Spring Boot) creates a Mockito mock and registers it in the test `ApplicationContext`, **replacing** any real bean of that type. It's great for isolating a layer — e.g., mocking a downstream client in a `@WebMvcTest`. The trap: `@MockBean`/`@SpyBean` are implemented as **context customizers**, so the *set* of mocks a class declares becomes part of the `MergedContextConfiguration` cache key. Two `@SpringBootTest` classes that differ only in which beans they mock get **two separate cached contexts** instead of sharing one — each paying full startup. Sprinkling ad-hoc mocks across many integration tests silently multiplies context builds. Mitigations: prefer plain Mockito in true unit tests (no context at all), consolidate common mocks into a shared base config so keys match, and use slices where mocking is naturally scoped. Note `@MockBean` is deprecated in favor of `@MockitoBean` in recent Spring versions, but the caching semantics are the same.
code
java · 19 lines// These two FORK separate contexts — different @MockBean sets => different cache keys:
@SpringBootTest
class CheckoutTest {
@MockBean PaymentGateway gateway; // mock set: {PaymentGateway}
}
@SpringBootTest
class ShippingTest {
@MockBean CarrierClient carrier; // mock set: {CarrierClient} -> new context
}
// Fix: same mock set via shared base => one cached context reused.
@SpringBootTest
abstract class BaseIT {
@MockBean PaymentGateway gateway;
@MockBean CarrierClient carrier; // identical set for all subclasses
}
class CheckoutIT extends BaseIT { /* only stubbing differs -> same context */ }
class ShippingIT extends BaseIT { /* reuses the same cached context */ }go deeper
Knows @MockBean puts a mock into the Spring context.
Understands it replaces the real bean and is used to isolate collaborators.
Knows it's a context customizer that participates in the cache key and engineers mock sets for reuse.
Sets policy: plain Mockito for unit tests, consolidated mock base classes for integration tests, to keep context count low across the org.
## What @MockBean is `@MockBean` is a Spring Boot test annotation that **creates a Mockito mock and puts it into the `ApplicationContext`**, replacing an existing bean of the same type (or adding one if absent). Companion `@SpyBean` wraps the *real* bean in a Mockito spy. It's the standard way to stub a collaborator inside a context-loading test — e.g., mock a `PaymentGateway` client so a `@WebMvcTest` controller test doesn't call the network. Usage: ```java @WebMvcTest(OrderController.class) class OrderControllerTest { @Autowired MockMvc mvc; @MockBean OrderService service; // real OrderService not loaded; mock injected } ``` ## The hidden performance trade-off `@MockBean`/`@SpyBean` are implemented via a **`ContextCustomizer`** (`MockitoContextCustomizer`). Context customizers participate in the **`MergedContextConfiguration`** that keys Spring's **static context cache**. Concretely: the **set** of mock/spy definitions on a test class is part of the cache key. Consequence: two otherwise-identical `@SpringBootTest` classes that mock **different** beans (or different combinations) produce **different keys** → **two separate contexts** are built and cached, each incurring full startup. Across a large suite, undisciplined `@MockBean` usage is a leading cause of **context proliferation** and slow test runs — even though each individual mock looks cheap. ## Mitigations 1. **Prefer plain Mockito unit tests** where no Spring context is needed at all — `Mockito.mock(...)` + constructor injection loads *nothing*, so it's both fastest and cache-neutral. 2. **Consolidate common mocks** into a **shared abstract base test class** so the mock set (and thus the cache key) is identical across subclasses → they share one context. 3. **Use slices** — mocking is naturally scoped, and slices already cluster into few reusable configs. 4. **Avoid one-off per-class mock variations** on full `@SpringBootTest`; if only the stubbed *behavior* differs, keep the same `@MockBean` set and change the `when(...)` stubs instead (stubs don't affect the key). ## Gotchas & notes - `@SpyBean` similarly fragments the cache. - Changing only the **stubbing** (`given(...)`/`when(...)`) inside a test does **not** create a new context — it's the *set of declared mocks*, not their behavior, that's in the key. - Mockito state is reset between tests for `@MockBean` (Spring resets it), so sharing a cached context across methods is safe. - In recent Spring Framework (6.2+) `@MockBean`/`@SpyBean` are **deprecated** in favor of core `@MockitoBean`/`@MockitoSpyBean`; behavior and caching semantics are equivalent. - A subtle correctness point: because `@MockBean` replaces a real bean, over-mocking in integration tests can hide wiring bugs — a speed *and* fidelity trade-off.
- Does changing what a @MockBean returns (given(...)) create a new context?No. Only the *set of declared* @MockBean/@SpyBean types is part of the cache key. Changing stubbed behavior with given(...)/when(...) inside a test reuses the same cached context.
- When should you avoid @MockBean entirely?When you don't need a Spring context at all — a plain unit test with Mockito.mock(...) and constructor injection loads nothing, runs fastest, and doesn't touch the context cache.
saying these in an interview costs you the question
- Thinking @MockBean is free performance-wise like a plain Mockito mock.
- Believing different stubbing behavior forks the context (it's the declared mock set that matters).
- Using @MockBean in a test that needs no Spring context at all.