skip to content

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