@SpringBootTest
Starts the whole application context for a test, optionally with a real server on a random port, so you test the app as it runs.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)- Added in Spring Boot Spring Boot 1.4
Parameters
| Parameter | Type | Description |
|---|---|---|
$webEnvironmentoptional | SpringBootTest.WebEnvironment= MOCK | MOCK, RANDOM_PORT, DEFINED_PORT or NONE: whether and how the web server runs. |
$properties / valueoptional | String[]= {} | key=value pairs added to the Environment before the context starts, with higher precedence than application.properties. |
$classesoptional | Class<?>[]= {} | Component classes to load instead of searching for the @SpringBootConfiguration class. |
$argsoptional | String[]= {} | Arguments passed to the application as if on the command line. |
$useMainMethodoptional | SpringBootTest.UseMainMethod= NEVER | ALWAYS, NEVER or WHEN_AVAILABLE: whether to start the context by calling the application's main method. |
Return value
n/aLoads the full application context for the test class, found from the nearest @SpringBootConfiguration, and optionally starts the embedded server.
Errors
The test fails at startup if no @SpringBootConfiguration is found or any bean cannot be created.
@SpringBootTest builds the same application context your main method builds: every component, every auto-configuration, your application.properties. It searches up the package tree for the class annotated with @SpringBootApplication and starts from there. Use it for integration tests that must prove the pieces work together, such as a request that goes through security, a controller, a transaction and a real database.
Choosing the web environment
webEnvironment decides whether a server runs:
MOCK(the default): a mock servlet environment and no port. Add@AutoConfigureMockMvcto send requests through MockMvc.RANDOM_PORT: the embedded server starts on a free port. Requests go over real HTTP, so filters, error pages and serialisation behave exactly as in production.DEFINED_PORT: usesserver.port. It fails when two builds run on the same machine, so avoid it in CI.NONE: no web layer, for testing services, jobs and listeners.
In Spring Boot 4 a running server does not give you an HTTP client by itself. Add @AutoConfigureRestTestClient (or @AutoConfigureTestRestTemplate) to get one bound to the random port.
Context caching
Starting a full context takes seconds. Spring's test framework caches each context and reuses it for every test class with the same configuration. Anything that changes the configuration makes a new context: different properties, a different set of @MockitoBean fields, a different @Import, an active profile. A suite with fifty slightly different @SpringBootTest classes starts fifty applications and takes minutes. Put shared setup on a base class or a meta-annotation and keep the variations rare.
Transactions and RANDOM_PORT
Putting @Transactional on a test class rolls back each test, but only for code on the test thread. With RANDOM_PORT the server handles the request on its own thread and commits, so rows the test inserted through HTTP stay in the database. Clean up explicitly, or use a fresh container per suite.
When not to use it
Not for every test. A service with mocked collaborators needs no Spring at all, and a controller test is faster and more focused with @WebMvcTest. Keep @SpringBootTest for the paths where wiring, configuration or the database is what you are checking.
Example
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureRestTestClient;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.web.servlet.client.RestTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// Normally in src/main/java: the application class the test searches for.
class CatalogueApplication {
public static void main(String[] args) {
SpringApplication.run(CatalogueApplication.class, args);
}
}
record Product(long id, String name) {}
("/api/products")
class ProductController {
("/{id}")
Product find( long id) {
return new Product(id, "Desk lamp");
}
}
// The real server starts on a free port; requests go over HTTP.
(webEnvironment = WebEnvironment.RANDOM_PORT)
class ProductApiTest {
RestTestClient client;
void returnsTheProductAsJson() {
client.get().uri("/api/products/7")
.exchange()
.expectStatus().isOk()
.expectBody(Product.class)
.isEqualTo(new Product(7, "Desk lamp"));
}
}
When to use it
- Check that a POST to /api/orders passes security, validation and the database and returns 201 with a Location header.
- Prove that a scheduled clean-up job deletes expired sessions using the real repository and transaction settings.
- Catch a missing bean or a broken property binding that only shows up when the whole application starts.
Frequently asked questions
Why does my @SpringBootTest say it cannot find a @SpringBootConfiguration?
The search starts at the test's package and walks up. If the test sits in a package that is not under your application class's package, nothing is found. Move the test into the same package tree, or point at the class with classes = MyApplication.class.
Should I use RANDOM_PORT or MOCK?
Use MOCK with MockMvcTester for most integration tests: it is faster and transactional rollback works. Use RANDOM_PORT when the real server matters, for example servlet filters registered by the container, HTTP compression, or behaviour on the error path.
How do I stop the suite starting the application many times?
Keep the context configuration identical across classes. Share @MockitoBean fields, imports and properties through a common base class or a custom annotation, and avoid @DirtiesContext unless a test really corrupts the context.
More examples
No web server and a test-only property
<code>WebEnvironment.NONE</code> skips the servlet container entirely, which suits services, scheduled jobs and message listeners. The <code>properties</code> attribute adds values with higher precedence than <code>application.properties</code>, but every distinct set of properties is a distinct cached context, so do not give each class its own.
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.stereotype.Service;
@Service
class InvoiceNumbers {
private final String prefix;
InvoiceNumbers(@Value("${app.invoices.prefix}") String prefix) {
this.prefix = prefix;
}
String next(long sequence) {
return prefix + "-" + String.format("%06d", sequence);
}
}
// No web server at all, and one property overridden for this class only.
@SpringBootTest(webEnvironment = WebEnvironment.NONE,
properties = "app.invoices.prefix=TEST")
class InvoiceNumbersTest {
@Autowired
InvoiceNumbers numbers;
@Test
void usesTheConfiguredPrefix() {
assertThat(numbers.next(42)).isEqualTo("TEST-000042");
}
}
Replacing one bean with a test configuration
A <code>@TestConfiguration</code> class is not picked up by component scanning, so it only applies where you <code>@Import</code> it. Here it supplies the <code>Clock</code> the application would otherwise create, which makes time-based rules testable without sleeping or mocking static methods. If the application also defines a <code>Clock</code> bean, give the test bean a different name or make it <code>@Primary</code>, because bean overriding is disabled by default.
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneOffset;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
@Component
class TrialPolicy {
private final Clock clock;
TrialPolicy(Clock clock) {
this.clock = clock;
}
boolean expired(Instant trialEnds) {
return clock.instant().isAfter(trialEnds);
}
}
// Adds a fixed clock on top of the application's own beans.
@TestConfiguration(proxyBeanMethods = false)
class FixedClockConfig {
@Bean
Clock clock() {
return Clock.fixed(Instant.parse("2026-03-01T09:00:00Z"), ZoneOffset.UTC);
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Import(FixedClockConfig.class)
class TrialPolicyTest {
@Autowired
TrialPolicy policy;
@Test
void trialThatEndedYesterdayHasExpired() {
assertThat(policy.expired(Instant.parse("2026-02-28T09:00:00Z"))).isTrue();
}
}
The default MOCK environment with MockMvcTester
With the default <code>MOCK</code> environment no port is opened. <code>@AutoConfigureMockMvc</code> registers <code>MockMvcTester</code>, which dispatches requests into the full context on the test thread. That makes a <code>@Transactional</code> rollback work again, at the cost of not exercising the real server.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
class StatusController {
@GetMapping("/api/status")
String status() {
return "ok";
}
}
// The default MOCK environment: the whole context, no port, MockMvc in front.
@SpringBootTest
@AutoConfigureMockMvc
class StatusEndpointTest {
@Autowired
MockMvcTester mvc;
@Test
void statusIsOk() {
mvc.get().uri("/api/status")
.assertThat()
.hasStatusOk()
.hasBodyTextEqualTo("ok");
}
}
Discussion