Why is @SpringBootTest considered expensive/slow, and what exactly does it load?
answer
- full ApplicationContext, not a unit
- scan + all auto-configs + bean wiring
- embedded server only if webEnvironment != MOCK
- cost = context build, not test body
- fix = caching + slices
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.
solid answer
~40 s@SpringBootTest boots the complete ApplicationContext the way the real app does: it runs component scanning, instantiates every @Component/@Service/@Repository, applies all matching Spring Boot auto-configurations (DataSource, JPA, security, Jackson, etc.), and — depending on webEnvironment — may start an embedded Tomcat/Netty server on a real or random port. That is a lot of work: reflection, classpath scanning, bean wiring, connection pools, EntityManagerFactory, etc. For a unit-sized assertion you pay a full-application startup cost (often 2–10+ seconds the first time). It's the right tool for true end-to-end / wiring tests, but wasteful when you only need one slice (a controller, or JPA). The fix is context caching and/or narrower slice annotations like @WebMvcTest or @DataJpaTest.
code
java · 19 lines// Loads the ENTIRE application context (all beans + auto-config).
// webEnvironment defaults to MOCK -> no real server/port.
@SpringBootTest
class OrderApplicationIT {
@Autowired
OrderService orderService; // whole graph is wired up to get this one bean
@Test
void placesOrder() {
assertThat(orderService.place(sampleOrder())).isNotNull();
}
}
// Starts a REAL embedded server on a random port (heavier still):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderEndToEndIT {
@Autowired TestRestTemplate rest;
}go deeper
Must know it loads the full context and is therefore slow, and that slices exist as a lighter alternative.
Should articulate the startup steps (scan, auto-config, wiring) and that the default webEnvironment is MOCK.
Frames cost as context-build cost and connects it to caching and slice choice as mitigations.
Reasons about suite-wide startup budget, context variation proliferation, and CI wall-clock as an architectural concern.
## What @SpringBootTest does `@SpringBootTest` is Spring Boot's integration-test annotation. When a test class is annotated with it, Spring builds a real **`ApplicationContext`** — the same container of beans that runs in production — before the tests execute. Building that context is expensive because it does the full startup sequence: 1. **Configuration discovery** — it finds your `@SpringBootApplication` class (or an explicit `classes = ...`) and reads its `@SpringBootConfiguration` + `@EnableAutoConfiguration` + `@ComponentScan`. 2. **Component scanning** — it scans your packages via reflection and registers every `@Component`, `@Service`, `@Repository`, `@Controller`, `@Configuration` as a bean definition. 3. **Auto-configuration** — `@EnableAutoConfiguration` evaluates *all* auto-config classes on the classpath (hundreds of them). Each is guarded by `@ConditionalOn...` checks. Matching ones create beans: a `DataSource` + Hikari pool, an `EntityManagerFactory` (Hibernate), Jackson `ObjectMapper`, Spring Security filter chain, Micrometer, etc. 4. **Bean instantiation & wiring** — every singleton bean is constructed and dependency-injected, `@PostConstruct` runs, `@Bean` methods execute. 5. **Embedded server (optional)** — controlled by `webEnvironment`: - `MOCK` (default) — a mock servlet environment, **no** real port/server. - `RANDOM_PORT` / `DEFINED_PORT` — starts a **real embedded Tomcat/Jetty/Netty** listening on a port. - `NONE` — no web environment at all. All of that for what might be a single assertion. First-time cost is commonly **2–10+ seconds**; multiply across dozens of test classes and the suite crawls. ## Why it matters The cost is not the *test body*, it's **context construction**. The mitigation strategies all attack that: - **Context caching** — Spring's `TestContext` framework caches contexts by configuration key and reuses them across test classes, so you pay the build cost once per *unique* configuration, not per class. - **Slices** — `@WebMvcTest`, `@DataJpaTest`, `@JsonTest`, `@WebFluxTest`, `@RestClientTest` etc. build a *minimal* context with only the beans that layer needs, skipping the rest of the app and auto-config. - **`@MockBean` / mocks** — replace heavy collaborators so you don't wire the whole graph. ## When to still use @SpringBootTest - True end-to-end tests that verify the whole app wires up. - Cross-cutting concerns spanning multiple layers. - Tests hitting a real embedded server via `TestRestTemplate` / `WebTestClient` with `RANDOM_PORT`. ## Gotchas - Each distinct `@SpringBootTest` configuration (different `@MockBean` sets, `@TestPropertySource`, active profiles, `webEnvironment`, `@DirtiesContext`) is a **separate cached context** — proliferation of variations kills caching and re-slows the suite. - Default `webEnvironment = MOCK` does **not** start a server; a common misconception is that `@SpringBootTest` always binds a port.
- Does @SpringBootTest start an embedded web server by default?No. The default is webEnvironment = MOCK, which provides a mock servlet environment with no real port. You only get a real embedded Tomcat/Netty when you set RANDOM_PORT or DEFINED_PORT.
- If the context build is the cost, why doesn't the second @SpringBootTest class pay it again?Because Spring's TestContext framework caches the context and reuses it for any test class whose configuration key matches — so identical configurations share one built context.
saying these in an interview costs you the question
- Thinking @SpringBootTest always starts a real server on port 8080 (default is MOCK, no server).
- Believing the slowness comes from the test logic rather than from building the ApplicationContext.
- Assuming a fresh context is created for every test method.