What attributes make up the TestContext cache key, and what causes two tests to NOT share a context?
answer
- MergedContextConfiguration = the key
- classes + profiles + properties + initializers + loader + parent
- Customizers: @MockitoBean, @DynamicPropertySource, webEnv
- Same bean mocked vs not = different context
- Log org.springframework.test.context.cache
basics
~10 sThe key is the merged test configuration: the config classes/locations, active profiles, property sources and inlined properties, context initializers, the context loader, and context customizers (like added mocks). Any difference means a separate context.
solid answer
~40 sSpring builds the cache key from the `MergedContextConfiguration` — the fully merged view of a test's setup. Its components are: the context configuration classes or resource locations, `ContextInitializer` classes, `@ActiveProfiles`, property source locations plus inlined `@TestPropertySource` properties, the `ContextLoader` used, the parent context (for hierarchies), and the set of `ContextCustomizer`s. Two tests share a context only if all of these are equal. The subtle part is customizers: `@MockBean`/`@MockitoBean` mock definitions, `@DynamicPropertySource` values, `@TestPropertySource`, web-environment settings, and `@MockitoSpyBean` all contribute customizers. So adding a mock to one test class, or inlining a property, gives it a distinct key and forces a brand-new context — a frequent, invisible cause of slow suites. Keeping configuration uniform across test classes is what maximizes cache hits.
code
java · 13 lines// Class A and Class B use the same config...
@SpringBootTest
class PaymentTest {
@MockitoBean OrderClient orderClient; // customizer -> key A
}
@SpringBootTest
class ShippingTest {
@MockitoBean WarehouseClient warehouse; // DIFFERENT customizer -> key B
}
// => TWO separate contexts, NOT one, purely because the mock
// definitions differ. Consolidating mocks in a shared base
// class would let them share a single cached context.go deeper
Know that any difference in config classes, profiles, or properties splits the cache.
List the MergedContextConfiguration components and explain that mocks and dynamic properties are customizers that split the key.
Reason about consolidating configs and mocks to maximize hits, and use cache logging to measure distinct contexts.
Set suite-wide conventions (base annotations, shared static containers, centralized mocks) so the whole org's tests converge on a few contexts.
## The cache key = MergedContextConfiguration The TestContext Framework doesn't key the cache on your test class — it keys on `org.springframework.test.context.MergedContextConfiguration`, the result of merging every configuration source that applies to the test (its own annotations plus inherited ones plus meta-annotations like `@SpringBootTest`). `MergedContextConfiguration.equals()`/`hashCode()` define cache identity. ## Components that make up the key 1. **Configuration classes / resource locations** — the `@ContextConfiguration(classes=...)` or `locations=...`, or what `@SpringBootTest` resolves to (typically the discovered `@SpringBootConfiguration`). 2. **Context initializer classes** — `ApplicationContextInitializer`s declared via `@ContextConfiguration(initializers=...)`. 3. **Active profiles** — from `@ActiveProfiles`. Order is normalized, but the *set* matters: `{test}` vs `{test,integration}` are different keys. 4. **Property source descriptors / locations** — `@TestPropertySource(locations=...)` files. 5. **Inlined properties** — `@TestPropertySource(properties = {"a=1"})`. Different inlined values ⇒ different key. 6. **Context loader** — the `ContextLoader`/`SmartContextLoader` class actually used. 7. **Parent configuration** — for `@ContextHierarchy`, the parent's merged config is part of the child's key. 8. **Context customizers** — a `Set<ContextCustomizer>` produced by registered `ContextCustomizerFactory` instances. This is the big one (below). ## Context customizers — the hidden key contributors `ContextCustomizer`s are equal-comparable objects folded into the key. Common sources: - **`@MockBean` / `@MockitoBean` / `@MockitoSpyBean`** — each distinct *set* of mock/spy definitions creates a distinct customizer, hence a distinct context. Two classes that mock different beans do **not** share a context; even the same bean mocked in one class but not another splits the cache. - **`@DynamicPropertySource`** — the resolved dynamic property values participate, so tests feeding different dynamic values (e.g. different Testcontainers) get different contexts. - **web environment** — `@SpringBootTest(webEnvironment=...)` (MOCK vs RANDOM_PORT) differs in the key. - **`@TestPropertySource`, `@ImportTestcontainers`, role/security test config**, etc. ## What does NOT change the key - The test **class name** itself is irrelevant — only configuration matters. - Individual **@Test methods** never change the key. - `@Sql`, `@Transactional`, and most execution-listener behavior don't alter the context identity (they act on an already-loaded context). ## Practical consequences - **Standardize** a small number of base configurations (e.g. a `@SpringBootTest` meta-annotation used everywhere) so most classes hit the same key. - **Beware mock sprawl:** scattering different `@MockitoBean` sets across many classes silently multiplies contexts. Consolidate mocks or push them to a shared base. - **@DynamicPropertySource with per-class containers** can defeat caching; share a static container across classes so the resolved values match. ## How to observe it Enable `DEBUG`/`TRACE` logging for `org.springframework.test.context.cache` — it logs cache size, hit count, miss count, and evictions, letting you see exactly how many distinct contexts your suite creates.
- Why can adding @MockitoBean to a test class slow the whole suite down?Each distinct set of mock definitions is a ContextCustomizer folded into the cache key, so that class can no longer reuse the plain context and forces Spring to build and cache an additional context. Many such classes multiply distinct contexts and startup cost.
- Do @ActiveProfiles({"a","b"}) and @ActiveProfiles({"b","a"}) produce different cache keys?No. Active profiles are treated as a set with normalized ordering, so the two are equal and share a context. But {a} vs {a,b} differ.
- How would you confirm how many contexts your suite creates?Turn on DEBUG logging for org.springframework.test.context.cache; it reports cache size, hits, misses, and evictions across the run.
saying these in an interview costs you the question
- Thinking the cache key is the test class name
- Believing @MockBean/@MockitoBean is free and doesn't affect caching
- Assuming inlined @TestPropertySource properties don't change the key
- Thinking @Transactional or @Sql create separate contexts