skip to content

@SpringBootTest & Full Context

Booting the whole application in a test: web environment modes, how the configuration is discovered, overriding properties, the cost of doing it, and the lighter ApplicationContextRunner. Interviewers ask when a full context is justified — and it usually is not.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

26

How does @SpringBootTest locate the configuration it uses to build the application context when you don't tell it explicitly?

level: juniorimportance: must knowfreq 70%

answer

  1. Search test package, walk UP to root
  2. Finds @SpringBootConfiguration (inside @SpringBootApplication)
  3. Main class in root package by convention
  4. No classes attr = discovery kicks in
  5. Full app context, not a slice

basics

~10 s

If you don't specify classes, @SpringBootTest searches the test's package and then walks up parent packages until it finds a class annotated with @SpringBootConfiguration (usually your @SpringBootApplication main class) and uses that.

solid answer

~40 s

When @SpringBootTest has no explicit configuration, it delegates to @SpringBootConfiguration detection: starting from the test class's package it searches upward through enclosing (parent) packages for exactly one class annotated with @SpringBootConfiguration. In practice that's your @SpringBootApplication class, because @SpringBootApplication is meta-annotated with @SpringBootConfiguration. That found class becomes the primary configuration, and because @SpringBootApplication also enables @ComponentScan and @EnableAutoConfiguration, the test bootstraps the same full context the real app uses. The conventional consequence: put your main application class in the root package (e.g. com.myapp) so every test underneath it discovers it automatically. If none is found — or more than one is — the context fails to start with a clear error.

code

java · 21 lines
java
// src/main/java/com/myapp/Application.java
package com.myapp; // root package

@SpringBootApplication // meta-annotated with @SpringBootConfiguration
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// src/test/java/com/myapp/web/OrderControllerTest.java
package com.myapp.web;

@SpringBootTest // no classes attribute -> walks up from com.myapp.web
class OrderControllerTest {                // -> finds com.myapp.Application
    @Autowired ApplicationContext ctx;

    @Test void contextLoads() {
        assertThat(ctx.containsBean("orderController")).isTrue();
    }
}

go deeper

for a junior

Know that omitting classes triggers a search up the package tree for the main app class.

for a middle

Explain @SpringBootConfiguration vs @SpringBootApplication and the root-package convention.

for a senior

Discuss failure modes (none/multiple found) and why the full auto-configured context results.

for a principal

Reason about package topology across multi-module builds and when to bypass discovery entirely.

### The problem it solves A Spring integration test needs to know *which* configuration to load to build the `ApplicationContext` (the container that holds your beans). Spring Boot lets you skip declaring this explicitly and instead *discovers* it by convention. ### The mechanism, step by step 1. `@SpringBootTest` (from `org.springframework.boot.test.context`) checks whether you gave it an explicit configuration via its `classes` attribute (or a nested `@Configuration`, or `@ContextConfiguration`). If you did, discovery is skipped. 2. If you did **not**, it falls back to `@SpringBootConfiguration`-detection, implemented by `SpringBootTestContextBootstrapper` / `AnnotatedClassFinder` (`SpringBootConfigurationFinder`). It starts in the **package of the test class** and looks for a class annotated with `@SpringBootConfiguration`. 3. If it doesn't find one there, it moves **up** to the parent package, then that package's parent, and so on toward the root, until it finds one. 4. `@SpringBootConfiguration` is a specialization of `@Configuration`. Your `@SpringBootApplication` class is annotated with it transitively — `@SpringBootApplication` is meta-annotated with `@SpringBootConfiguration`, `@EnableAutoConfiguration`, and `@ComponentScan`. So the found class is almost always your main app class. ### Why the root-package convention matters Because the search only goes **upward** (toward the root), the main class must sit at or above every test's package. The idiomatic layout is: main class in the root package `com.myapp`, everything else (`com.myapp.web`, `com.myapp.service`) beneath it. Then any test in `com.myapp.**` discovers it. If you bury the main class deep in a sub-package, tests in sibling packages won't find it. ### What you actually get Because the discovered class is `@SpringBootApplication`, the test context includes component scanning of your packages **and** auto-configuration — i.e. the *full* application context, wired much like production (minus the embedded web server unless you ask for one via `webEnvironment`). ### Failure modes - **None found:** startup fails with `Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test`. - **More than one found** in scope: it fails because the primary configuration is ambiguous. ### When to override If you don't want the full app config — or you have a stripped-down test module — pass `@SpringBootTest(classes = SomeConfig.class)` to bypass discovery entirely.

  • What annotation is @SpringBootConfiguration a specialization of, and why does that matter?
    It's a specialization of @Configuration, so a @SpringBootConfiguration class is a full Java configuration class. It matters because discovery keys off @SpringBootConfiguration specifically (not any @Configuration), which is why only the main app class — not arbitrary config classes — is auto-selected.
  • What happens if the finder locates two @SpringBootConfiguration classes?
    The context fails to start because the primary configuration is ambiguous — Spring Boot expects exactly one. You typically hit this only if you accidentally added a second @SpringBootApplication (e.g. a test-only one) in scope; fix it by removing the duplicate or specifying classes explicitly.

saying these in an interview costs you the question

  • Saying it scans the whole classpath for any @Configuration (it searches up the package tree for @SpringBootConfiguration specifically)
  • Claiming it searches downward into sub-packages
  • Thinking it looks for @SpringBootApplication by name rather than the @SpringBootConfiguration meta-annotation

context

open as a page

Why is @SpringBootTest considered expensive/slow, and what exactly does it load?

level: juniorimportance: must knowfreq 70%

basics

~10 s

@SpringBootTest starts the full Spring application context — every bean, all auto-configurations, and (optionally) an embedded web server. Building that whole context takes seconds, so tests run slowly.

open as a page

What does the `properties = {}` attribute of `@SpringBootTest` do, and when would you use it?

level: juniorimportance: must knowfreq 70%

basics

~10 s

It sets configuration properties just for that test, in key=value form. Those values override the same keys from application.properties, so you can test the app under specific config without editing real files.

open as a page

What are the four webEnvironment modes of @SpringBootTest, and what does each one do?

level: juniorimportance: must knowfreq 70%

basics

~10 s

MOCK (default) loads a mock servlet environment, no real port. RANDOM_PORT and DEFINED_PORT start a real embedded server on a random or fixed port. NONE loads the context with no web environment at all.

open as a page

How do you use ApplicationContextRunner to verify a @ConditionalOnProperty-guarded bean appears only when the property is set? Show the assertions.

level: middleimportance: must knowfreq 38%

basics

~10 s

Chain withPropertyValues("feature.enabled=true") before run(), then inside the lambda assert assertThat(context).hasSingleBean(Foo.class). For the off case, omit or set the property false and assert doesNotHaveBean(Foo.class).

open as a page

Instead of @SpringBootTest, which slice would you pick for a controller vs a repository, and what does each load?

level: middleimportance: must knowfreq 68%

basics

~10 s

Use @WebMvcTest for a controller — it loads only the web/MVC layer and mocks the rest. Use @DataJpaTest for a repository — it loads JPA/Hibernate plus an in-memory (or configured) database, nothing else.

open as a page

When would you use MOCK + MockMvc versus RANDOM_PORT + TestRestTemplate, and what does each actually exercise?

level: middleimportance: must knowfreq 68%

basics

~10 s

Use MOCK + MockMvc for fast in-process controller tests that never touch the network. Use RANDOM_PORT + TestRestTemplate when you need a real embedded server and real HTTP requests to test the full stack.

open as a page

How does Spring's TestContext caching work, and what causes it to build a new context instead of reusing one?

level: seniorimportance: must knowfreq 60%

basics

~20 s

Spring caches each ApplicationContext keyed by its configuration. Tests with an identical configuration reuse the cached context; any difference in the key (classes, profiles, properties, @MockBean set, web environment) triggers building a new, separately cached context.

open as a page

What is `@DynamicPropertySource` for, and how does it interact with `@TestPropertySource` / `@SpringBootTest(properties=...)`?

level: seniorimportance: must knowfreq 55%

basics

~10 s

@DynamicPropertySource registers properties whose values are only known at runtime — like a Testcontainers JDBC URL — via a static method taking a DynamicPropertyRegistry. Its values have the highest precedence, above @TestPropertySource and @SpringBootTest(properties).

open as a page

What is Spring Boot's ApplicationContextRunner, and why would you use it instead of @SpringBootTest to test auto-configuration?

level: juniorimportance: should knowfreq 32%

basics

~10 s

ApplicationContextRunner is a Spring Boot test helper that boots a tiny, isolated ApplicationContext per test. You use it to check which beans your auto-configuration creates, without the cost of a full @SpringBootTest.

open as a page

When and how would you use the classes attribute of @SpringBootTest to override configuration detection?

level: middleimportance: should knowfreq 55%

basics

~10 s

Set @SpringBootTest(classes = {MyConfig.class}) to tell the test exactly which @Configuration classes to load. This bypasses the package-tree search entirely, so the context contains only what those classes define plus what they import.

open as a page

What are WebApplicationContextRunner and ReactiveWebApplicationContextRunner for, and what does the runner's immutability mean for how you reuse it across tests?

level: middleimportance: should knowfreq 22%

basics

~20 s

The Web/Reactive variants build a servlet or reactive web ApplicationContext so you can test conditions like @ConditionalOnWebApplication. The runner is immutable: every with* returns a new instance, so a shared base runner is safe and you must use the returned copy.

open as a page

What does the `args = {}` attribute of `@SpringBootTest` do, and how do those values reach the application?

level: middleimportance: should knowfreq 40%

basics

~10 s

args is the String[] passed to SpringApplication.run(args), exactly like real command-line arguments. Option args like --app.mode=fast become properties; you can also inject the whole ApplicationArguments bean to read them.

open as a page

How does `@TestPropertySource` work, and how do its `locations` and inlined `properties` relate to `@SpringBootTest(properties=...)`?

level: middleimportance: should knowfreq 45%

basics

~10 s

@TestPropertySource adds test-only property sources: locations loads .properties files, and properties inlines key=value pairs. Inlined properties beat locations, and both override application.properties. @SpringBootTest(properties=...) is essentially the same inlined mechanism.

open as a page

How do you obtain the actual port under RANDOM_PORT, and how does @LocalServerPort differ from server.port?

level: middleimportance: should knowfreq 52%

basics

~10 s

Inject an int field annotated with @LocalServerPort (or read the ${local.server.port} property). It holds the real random port the embedded server bound to at runtime, which under RANDOM_PORT differs from any configured server.port.

open as a page

Contrast the full context @SpringBootTest builds via configuration detection with a sliced test like @WebMvcTest. When does detection still apply?

level: seniorimportance: should knowfreq 60%

basics

~20 s

@SpringBootTest loads the full application context (all your beans + auto-configuration) by discovering the @SpringBootConfiguration class. A slice like @WebMvcTest loads only a narrow layer. Both still find the main class the same way to know where to start.

open as a page

How do you assert on a context that fails to start with ApplicationContextRunner, and when should you prefer the runner over @SpringBootTest?

level: seniorimportance: should knowfreq 20%

basics

~10 s

Inside run(), the failure is captured not thrown. Use assertThat(context).hasFailed() and assertThat(context).getFailure() to inspect the startup exception (e.g. rootCause / message). Prefer the runner over @SpringBootTest for fast, isolated auto-config/condition tests.

open as a page

How do you use ApplicationContextRunner with FilteredClassLoader to test @ConditionalOnClass / @ConditionalOnMissingClass back-off?

level: seniorimportance: should knowfreq 24%

basics

~10 s

Use withClassLoader(new FilteredClassLoader(SomeLib.class)) to hide a class as if it weren't on the classpath, then run() and assert the conditionally-created bean is absent — proving @ConditionalOnClass backed off.

open as a page

What is @MockBean, and what is the hidden performance trade-off of using it heavily across @SpringBootTest classes?

level: seniorimportance: should knowfreq 45%

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.

open as a page

When is WebEnvironment.NONE the right choice, and how does it differ from a plain non-web @SpringBootTest?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Use NONE when you want the full application context but no web layer — for testing services, repositories, or scheduled/messaging beans. It loads via SpringApplication with no servlet context, no embedded server, and no port.

open as a page

What's the difference between registering a class via withConfiguration(AutoConfigurations.of(...)) versus withUserConfiguration(...) in a runner, and why does it matter for @ConditionalOnMissingBean?

level: principalimportance: should knowfreq 18%

basics

~10 s

AutoConfigurations.of(...) registers classes as auto-configuration — processed last, in auto-config order, honoring @AutoConfigureAfter/Before. withUserConfiguration registers them as ordinary user beans, processed first. That ordering is exactly what @ConditionalOnMissingBean depends on.

open as a page

You're the tech lead on a service whose test suite takes 12 minutes, dominated by dozens of @SpringBootTest classes. How do you diagnose and cut the cost without losing coverage?

level: principalimportance: should knowfreq 38%

basics

~20 s

Measure how many distinct contexts are being built, convert most @SpringBootTest classes to slices or a shared base config, eliminate needless @DirtiesContext and per-class property/mock variations so tests reuse cached contexts, and reserve full @SpringBootTest for a few genuine end-to-end tests.

open as a page

Lay out the property-source precedence across `@DynamicPropertySource`, `@TestPropertySource`, `@SpringBootTest(properties)`, `args`, and application config — and explain how `spring.main.*` overrides fit in.

level: principalimportance: should knowfreq 30%

basics

~10 s

Highest to lowest in a test: @DynamicPropertySource > @TestPropertySource/@SpringBootTest(properties) > command-line args > system properties/env > application.properties. spring.main.* keys configure SpringApplication itself and can be set through any of these, so precedence still applies.

open as a page

Under RANDOM_PORT, a @Transactional test issues an HTTP POST that creates a row, yet the row isn't rolled back after the test. Why, and how do you handle it?

level: principalimportance: should knowfreq 38%

basics

~20 s

The embedded server handles the request on a different thread with its own transaction, so the test's transaction can't wrap or roll it back. Clean up explicitly — @Sql, delete in teardown, @DirtiesContext, or reset the datastore.

open as a page

How do @TestConfiguration and nested static @Configuration classes interact with @SpringBootTest's detected configuration?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

A nested static @Configuration inside the test is picked up automatically and supplements the detected config. @TestConfiguration is additive too but only applies when imported or when it's a nested class of the test — it adds or overrides beans without replacing the discovered configuration.

open as a page

A developer moved the @SpringBootApplication class into a sub-package and now several @SpringBootTest tests fail at startup. Diagnose why and give the fixes.

level: principalimportance: nice to knowfreq 35%

basics

~20 s

Detection only searches upward from the test's package. If the main class now sits in a sibling sub-package (not an ancestor of the tests), the search reaches the root without finding @SpringBootConfiguration and fails with 'Unable to find a @SpringBootConfiguration'. Fix: restore the root-package layout or set classes explicitly.

open as a page