@SpringBootTest & the Test Context
@SpringBootTest & the Test Context
Unit tests with plain JUnit 5 and Mockito work well for isolated logic, but sooner or later you need to verify that your beans wire together correctly, that your configuration properties load, or that a real HTTP request reaches the right handler. That is where @SpringBootTest comes in: it bootstraps a full (or partial) Spring ApplicationContext inside your test, giving you the same IoC container your production application runs with.
What @SpringBootTest Actually Does
When you annotate a test class with @SpringBootTest, Spring Boot's test infrastructure does the following:
- Locates your main
@SpringBootApplicationclass by walking up the package tree from the test class. - Builds the full
ApplicationContext— scanning components, processing@Configurationclasses, running auto-configuration, and bindingapplication.properties/application.yml. - Injects beans into the test class via
@Autowired, just like any other Spring-managed bean.
@SpringBootTest creates the full context but does NOT listen on a port unless you set webEnvironment. For tests that only need to call service or repository beans directly, the default (no server) is faster and perfectly sufficient.
The simplest possible integration test looks like this:
The webEnvironment Attribute
@SpringBootTest accepts a webEnvironment parameter that controls whether and how an embedded server is started:
MOCK(default): Loads aWebApplicationContextwith a mock Servlet environment. No real port. Use withMockMvc.RANDOM_PORT: Starts the embedded server on a random free port. Ideal for full end-to-end HTTP tests usingTestRestTemplateorWebTestClient. Port is injected with@LocalServerPort.DEFINED_PORT: Starts on the port defined inapplication.properties(default 8080). Risky in CI — two tests running in parallel would clash.NONE: Loads anApplicationContextwith no web environment at all. Useful for testing service-layer beans in a non-web app.
Context Caching — The Most Important Performance Feature
Bootstrapping a Spring context takes time: component scanning, auto-configuration, HikariCP pool creation, Hibernate schema validation — all of this can add 2–5 seconds per context. If every test class created its own context, a suite of 200 tests could easily spend more time in setup than in assertions.
Spring's test framework solves this with context caching: a context is started once per unique configuration key and reused by every test class that shares that key. The cache key is derived from:
- The set of context configuration annotations (
@SpringBootTest,@ContextConfiguration, etc.). - The
webEnvironmentvalue. - Any active profiles (
@ActiveProfiles). - Any property overrides (
propertiesattribute or@TestPropertySource). - Any
@MockBeanor@SpyBeandeclarations (each unique combination creates a separate context).
@MockBean declarations to the minimum necessary — every new @MockBean combo forces a fresh context.
Using a Dedicated Test Application Properties File
Production databases and third-party API keys must not be used in tests. Place an src/test/resources/application.properties (or application-test.yml) file alongside your test sources. Spring Boot's test slice picks it up automatically and it takes precedence over the main one when running tests.
If only a handful of tests need special properties, use the inline properties attribute instead of a file — it keeps the override visible next to the annotation:
@DirtiesContext unless you truly need it. This annotation tells Spring to destroy and rebuild the context after the annotated test (class or method). It guarantees isolation but completely defeats context caching — and is one of the most common causes of slow test suites. Prefer database rollback (via @Transactional on the test method) or Testcontainers lifecycle management for isolation.
When to Use @SpringBootTest vs Sliced Tests
Spring Boot ships several focused test slices — @WebMvcTest, @DataJpaTest, @JsonTest, and others — that load only the portion of the context relevant to one layer. @SpringBootTest is the right choice when:
- You need to test the integration of multiple layers (e.g. controller → service → repository).
- You are testing auto-configuration behaviour — checking that your
application.propertiescorrectly initialises a complex bean. - You are writing smoke tests that confirm the application context loads without error (
contextLoads()).
For tests that target a single layer in isolation, prefer the appropriate slice annotation — it starts faster and pins failures to the correct layer. A well-structured test suite uses both: sliced tests for the majority of coverage and a small number of @SpringBootTest integration tests to verify the wiring end-to-end.
Summary
@SpringBootTest bootstraps the complete ApplicationContext for your tests, with fine-grained control over the web environment through the webEnvironment attribute. Context caching means the expensive startup cost is paid once per unique configuration — so structuring your tests to share configuration is the single biggest lever for keeping your integration test suite fast. Use a dedicated application.properties in src/test/resources to swap production infrastructure (databases, queues, external APIs) for lightweight test doubles, and reach for @SpringBootTest when you need to verify cross-layer wiring rather than isolated component behaviour.