What are the ApplicationContext-caching and performance implications of @MockitoSpyBean?
answer
- Overrides are part of the context cache key
- Different spy set → new cached context
- Contexts are expensive to build (LRU, ~32 default)
- MockitoTestExecutionListener resets spies each method
- Consolidate overrides in base classes
basics
~20 sEach unique set of bean overrides makes Spring build and cache a separate ApplicationContext. Spread @MockitoSpyBean across many different test classes and you get more context builds, slowing the suite. Keeping override sets consistent lets tests share a cached context.
solid answer
~40 sBean-override annotations like @MockitoSpyBean and @MockitoBean become part of the test's context cache key (the MergedContextConfiguration). Two test classes that override the same beans identically can share one cached context; any difference forces a new context to be built and held in the cache. Since spinning up a Spring context is expensive, scattering different spy/mock combinations across classes multiplies context builds and memory. Mitigations: standardize overrides into shared base classes or test slices, prefer real beans or test doubles wired via configuration when possible, and be aware the spy also needs Mockito state reset between tests (Spring's MockitoTestExecutionListener resets mocks/spies automatically each method). Overuse is a recognized test-suite smell.
go deeper
May not know about context caching at all.
Aware contexts are cached but fuzzy on what invalidates the cache.
Must explain overrides are in the cache key and the consolidation strategies.
Sets suite-wide policy: minimize distinct contexts, standardize override sets, monitor cache stats, treat spy sprawl as tech debt.
**Context caching** is the Spring TestContext Framework optimization that reuses a built `ApplicationContext` across test classes/methods instead of rebuilding it every time. The cache key is the **MergedContextConfiguration** — a fingerprint of everything that defines the context: configuration classes, active profiles, property sources, context initializers, and crucially the set of **bean overrides**. **Why @MockitoSpyBean affects the key.** When you add `@MockitoSpyBean` (or `@MockitoBean`), you are declaring a bean override. That override is folded into the cache key. Consequences: - Two test classes with *identical* configuration and *identical* overrides → **same** cached context, reused, fast. - Any difference in the set of overridden beans → a **distinct** cache key → Spring builds and stores a **separate** context. Building a context (component scan, bean instantiation, auto-config) is one of the most expensive things in a Spring test suite. So a codebase where many test classes each declare a slightly different spy/mock mix ends up with many cached contexts: more startup time and more heap (the cache default holds up to 32 contexts, evicting LRU — exceeding it causes churn and re-builds). **Mockito state management.** Spring registers `MockitoTestExecutionListener`, which initializes the mocks/spies before each test and **resets** them afterward, so stubs and recorded invocations don't leak between methods. This is why you rarely call `Mockito.reset()` manually. (Under the hood it uses `MockitoAnnotations`/`MockReset` semantics.) **Performance mitigations:** 1. **Consolidate overrides** — put a shared set of `@MockitoSpyBean`/`@MockitoBean` declarations in a common base class or a meta-annotation so many tests share one context. 2. **Prefer test slices** (`@WebMvcTest`, `@DataJpaTest`) with a *stable* override set. 3. **Avoid gratuitous spies** — if you can wire a real lightweight double via a `@TestConfiguration`, you may keep contexts shareable and avoid Mockito entirely. 4. **Watch the cache statistics** — Spring logs context cache hit/miss stats at DEBUG (`ContextCache` / hit-miss counts) to spot thrashing. **Edge cases:** - `@DirtiesContext` forces eviction and a rebuild — combining it with spies compounds cost. - Different `name` targets or different declared types count as different overrides. - The override changes the *bean definition/instance* in that context, so it's not something you can toggle per-method without a new context. **Bottom line for a senior:** treat every distinct `@MockitoSpyBean` combination as a potential new context; design the suite so override sets cluster, and reach for spies deliberately, not reflexively.
- Do you need to manually reset a @MockitoSpyBean between test methods?Usually no. Spring's MockitoTestExecutionListener resets the mocks/spies before/after each test method, clearing stubs and recorded invocations, so state doesn't leak. Manual Mockito.reset() is rarely needed and can even hide design issues.
- How can two test classes share a context despite using spies?Give them identical MergedContextConfiguration — same config classes, profiles, properties, and the exact same set of @MockitoSpyBean/@MockitoBean overrides (often via a shared base class or composed annotation). Then the cache key matches and the context is reused.
saying these in an interview costs you the question
- Assuming @MockitoSpyBean is free / doesn't affect context caching
- Manually calling Mockito.reset() everywhere, unaware the listener does it
- Thinking each test method rebuilds the context regardless of overrides