@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.

Signature
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
  • Added in Spring Boot Spring Boot 1.4

Parameters

ParameterTypeDescription
$webEnvironmentoptionalSpringBootTest.WebEnvironment= MOCKMOCK, RANDOM_PORT, DEFINED_PORT or NONE: whether and how the web server runs.
$properties / valueoptionalString[]= {}key=value pairs added to the Environment before the context starts, with higher precedence than application.properties.
$classesoptionalClass<?>[]= {}Component classes to load instead of searching for the @SpringBootConfiguration class.
$argsoptionalString[]= {}Arguments passed to the application as if on the command line.
$useMainMethodoptionalSpringBootTest.UseMainMethod= NEVERALWAYS, 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 @AutoConfigureMockMvc to 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: uses server.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

Example · java
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.
@SpringBootApplication
class CatalogueApplication {
    public static void main(String[] args) {
        SpringApplication.run(CatalogueApplication.class, args);
    }
}

record Product(long id, String name) {}

@RestController
@RequestMapping("/api/products")
class ProductController {

    @GetMapping("/{id}")
    Product find(@PathVariable long id) {
        return new Product(id, "Desk lamp");
    }
}

// The real server starts on a free port; requests go over HTTP.
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestTestClient
class ProductApiTest {

    @Autowired
    RestTestClient client;

    @Test
    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.

Example · java
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.

Example · java
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.

Example · java
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

  • Be the first to comment on this lesson.