How does the TestContext Framework resolve which ContextLoader to use, and what is SmartContextLoader / DelegatingSmartContextLoader?
answer
- ContextLoader SPI -> SmartContextLoader (merged model)
- default = DelegatingSmartContextLoader
- AnnotationConfig (classes) + GenericXml/Groovy (locations)
- both classes+locations -> IllegalStateException
- @WebAppConfiguration -> WebDelegatingSmartContextLoader
basics
~10 sIf you don't set loader, Spring uses DelegatingSmartContextLoader by default. It delegates to AnnotationConfigContextLoader for classes and GenericXmlContextLoader/GenericGroovyXmlContextLoader for locations, picking whichever matches your declaration.
solid answer
~30 s`ContextLoader` is the SPI that actually builds the ApplicationContext from a `MergedContextConfiguration`. `SmartContextLoader` is the newer sub-interface that understands both classes and resource locations and can do convention detection. When `@ContextConfiguration` has no explicit `loader`, the framework picks `DelegatingSmartContextLoader`, which wraps `AnnotationConfigContextLoader` (component classes) and `GenericXmlContextLoader` plus `GenericGroovyXmlContextLoader` (XML/Groovy). It delegates to whichever candidate supports the declared config, and if none is declared it runs each loader's detection. With `@WebAppConfiguration` the default becomes `WebDelegatingSmartContextLoader`, producing a `WebApplicationContext`. You can override with an explicit `loader`, but a custom loader replaces this delegation, so you lose automatic classes-vs-locations selection.
code
java · 18 lines// Default: no loader set -> DelegatingSmartContextLoader chooses
// AnnotationConfigContextLoader because only `classes` is present.
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = AppConfig.class)
class DefaultLoaderTest { }
// Explicit loader overrides delegation entirely.
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = AppConfig.class,
loader = AnnotationConfigContextLoader.class)
class ExplicitLoaderTest { }
// Web: @WebAppConfiguration flips default to WebDelegatingSmartContextLoader
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebConfig.class)
class WebLoaderTest { }go deeper
Just know a ContextLoader builds the context and you rarely set it yourself.
Name DelegatingSmartContextLoader and its two candidate loaders and how it picks based on classes vs locations.
Explain the SmartContextLoader vs ContextLoader distinction, the ambiguity exception, and the web variant.
Relate it to how @SpringBootTest layers SpringBootContextLoader and when a custom AbstractContextLoader is justified.
## The loader SPI `org.springframework.test.context.ContextLoader` is the strategy interface the TestContext Framework uses to **construct** the `ApplicationContext`. The original interface exposed `processLocations(...)` and `loadContext(String... locations)` — string-oriented, XML-era. `org.springframework.test.context.SmartContextLoader` **extends** `ContextLoader` and is the modern replacement. It works against the richer `ContextConfigurationAttributes` / `MergedContextConfiguration` model and adds: - `processContextConfiguration(ContextConfigurationAttributes)` — a hook to *detect* defaults (nested @Configuration classes or convention XML) and normalize declared config. - `loadContext(MergedContextConfiguration)` — build the context from the fully merged config (classes + locations + initializers + profiles + parent). Because it sees the merged model, a `SmartContextLoader` can support **both** component classes and resource locations, which the old `ContextLoader` could not. ## Resolution order When the framework needs a loader for a `@ContextConfiguration`: 1. **Explicit `loader` attribute** — if you set `@ContextConfiguration(loader = MyLoader.class)`, that concrete loader is used, period. 2. **Inherited loader** — if a superclass test declared a loader and `inheritLocations`/inheritance applies, it can be inherited. 3. **Default** — otherwise the framework uses a **delegating** loader: - `DelegatingSmartContextLoader` for standard tests. - `WebDelegatingSmartContextLoader` when `@WebAppConfiguration` is present (yields a `GenericWebApplicationContext`). ## How DelegatingSmartContextLoader delegates `DelegatingSmartContextLoader` internally holds two candidate `SmartContextLoader`s: - `AnnotationConfigContextLoader` — handles **component/@Configuration classes**. - `GenericXmlContextLoader` — handles **XML** resource locations. (`GenericGroovyXmlContextLoader` is also used when Groovy is on the classpath and a `.groovy` resource is present.) For a given declaration it asks each candidate whether it *supports* the config: - If only `classes` are declared -> `AnnotationConfigContextLoader`. - If only `locations` are declared -> the XML/Groovy loader. - If **nothing** is declared -> each candidate runs its detection (nested @Configuration, or `<Test>-context.xml`). **Gotcha:** if a single `@ContextConfiguration` somehow ends up with *both* classes and locations, `DelegatingSmartContextLoader` cannot decide and throws an `IllegalStateException` — it deliberately refuses ambiguous input. This is why you keep one style per level. ## Web variant Adding `@WebAppConfiguration` switches the default to `WebDelegatingSmartContextLoader`, whose candidates are `AnnotationConfigWebContextLoader` and `GenericXmlWebContextLoader`, producing a `WebApplicationContext` with a mock `ServletContext`. ## Custom loaders You can implement `AbstractGenericContextLoader` / `AbstractContextLoader` to customize context creation (e.g., custom `BeanDefinitionReader`). Setting `loader` **replaces** the delegating behavior, so a custom loader that only understands one config style loses the automatic classes-vs-locations dispatch — a common cause of 'my classes attribute is ignored' surprises. ## When it matters in interviews Mostly you never touch this — the defaults 'just work'. It becomes relevant when: writing a custom test infrastructure, debugging why a loader ignores your config, or explaining how `@SpringBootTest` layers `SpringBootContextLoader` on top of this SPI.
- What happens if a single @ContextConfiguration declares both `classes` and `locations`?DelegatingSmartContextLoader treats it as ambiguous and throws an IllegalStateException, because neither of its candidate loaders can claim a config that mixes both styles.
- How is SmartContextLoader different from the original ContextLoader interface?ContextLoader was string/locations oriented (processLocations, loadContext(String...)). SmartContextLoader works against the merged model, supports component classes and locations together, and can perform convention-based default detection.
saying these in an interview costs you the question
- Saying the default loader is AnnotationConfigContextLoader directly (it's DelegatingSmartContextLoader which delegates to it)
- Claiming DelegatingSmartContextLoader happily loads both classes and locations at once
- Not knowing @WebAppConfiguration changes the loader to the web variant