Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/spring-projects/spring-boot/llms.txt

Use this file to discover all available pages before exploring further.

Spring Boot ships a rich testing toolkit that covers everything from fast unit tests to full integration tests with real infrastructure. This guide covers the most common testing tasks, including running tests from the command line, isolating test configuration, using Testcontainers for zero-config database setup, and controlling test execution order.

Run tests with Maven and Gradle

Run all tests in the project:
./mvnw test
Run tests in a specific class:
./mvnw test -Dtest=MyIntegrationTests
Run a single test method:
./mvnw test -Dtest=MyIntegrationTests#shouldReturnUser
Skip tests during a package build:
./mvnw package -DskipTests

Configure test properties without application.properties

Use @TestPropertySource to supply test-specific values without modifying application.properties. The annotation accepts inline properties or a path to a separate .properties file.
MyIntegrationTests.java
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

@SpringBootTest
@TestPropertySource(properties = {
    "spring.datasource.url=jdbc:h2:mem:testdb",
    "myapp.feature.enabled=true"
})
class MyIntegrationTests {

    @Test
    void contextLoads() {
    }

}
To load properties from a file:
@TestPropertySource(locations = "classpath:test-overrides.properties")
For @SpringBootTest you can also pass properties directly via the annotation’s properties attribute, which behaves identically to @TestPropertySource(properties = ...) for simple cases.

Use @DirtiesContext to reset the application context

By default, Spring caches the ApplicationContext across tests for efficiency. If a test modifies shared state (database records, singleton beans, static fields), annotate it with @DirtiesContext to force a fresh context for subsequent tests.
MyStatefulTests.java
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;

@SpringBootTest
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyStatefulTests {

    @Test
    void firstTest() {
        // modifies shared state
    }

    @Test
    void secondTest() {
        // runs with a fresh context
    }

}
Using @DirtiesContext frequently is expensive because creating a new ApplicationContext is slow. Prefer test isolation through database transactions (use @Transactional on your test class to roll back after each test) or by scoping state to the test method itself.

Set up databases with Testcontainers and @ServiceConnection

@ServiceConnection eliminates manual connection property configuration. Annotate a container field with @ServiceConnection and Spring Boot automatically creates the matching ConnectionDetails bean, overriding any spring.datasource.* or equivalent properties.
1

Add the Testcontainers dependencies

pom.xml
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-testcontainers</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>postgresql</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>junit-jupiter</artifactId>
  <scope>test</scope>
</dependency>
2

Declare the container as a Spring bean

Declare the container in a @TestConfiguration class so Spring manages its lifecycle. Spring starts the container before any bean that depends on it and stops it after the application context shuts down:
MyTestConfiguration.java
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.testcontainers.containers.PostgreSQLContainer;

@TestConfiguration(proxyBeanMethods = false)
class MyTestConfiguration {

    @Bean
    @ServiceConnection
    PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:16");
    }

}
3

Import the configuration in your test class

MyIntegrationTests.java
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Import;

@SpringBootTest
@Import(MyTestConfiguration.class)
class MyIntegrationTests {

    @Test
    void contextLoads() {
    }

}
@ServiceConnection is supported for PostgreSQL, MySQL, MariaDB, MongoDB, Redis, Kafka, RabbitMQ, Elasticsearch, Neo4j, Cassandra, and many others. The full list is in the spring-boot-testcontainers module. For generic containers (those using GenericContainer), provide the image name via @ServiceConnection(name = "redis").

Mock external services with @MockBean

@MockBean adds a Mockito mock to the Spring ApplicationContext, replacing the real bean for the duration of the test. Use it to isolate your code from external HTTP services, messaging brokers, or other infrastructure.
MyServiceTests.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.test.mock.mockito.MockBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

@SpringBootTest
class MyServiceTests {

    @Autowired
    private MyService myService;

    @MockBean
    private ExternalApiClient externalApiClient;

    @Test
    void shouldReturnMockedData() {
        given(externalApiClient.fetchData()).willReturn("mocked-response");
        String result = myService.processData();
        assertThat(result).isEqualTo("processed: mocked-response");
    }

}
@MockBean causes the application context to be considered unique (different from contexts without the mock), so Spring will not reuse a cached context. If context startup time matters, consider using constructor injection and passing a mock directly in a plain unit test instead.

Test with Spring Security

Spring Security provides test support that lets you run a test as a specific user without going through the authentication flow:
MySecurityTests.java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
class MySecurityTests {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithMockUser(roles = "ADMIN")
    void adminEndpointIsAccessible() throws Exception {
        mockMvc.perform(get("/admin/users"))
               .andExpect(status().isOk());
    }

    @Test
    void adminEndpointRequiresAuthentication() throws Exception {
        mockMvc.perform(get("/admin/users"))
               .andExpect(status().isUnauthorized());
    }

}

Structure @Configuration classes for slice tests

Slice tests (such as @WebMvcTest or @DataJpaTest) restrict component scanning to a limited set of bean types. Beans defined in @Configuration classes outside the scanned types are not automatically included.
Importing a large @Configuration class into a slice test loads all its beans, including ones irrelevant to the slice (for example, a DataSource bean in a @WebMvcTest). This slows down the test and can cause startup failures.
Avoid this by splitting configuration into small, focused classes:
MySecurityConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
class MySecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // security configuration only
        return http.build();
    }

}
MyDatasourceConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
class MyDatasourceConfiguration {

    @Bean
    DataSource dataSource() {
        // datasource configuration only
        return ...;
    }

}
Import only what the slice test needs:
MyControllerTests.java
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;

@WebMvcTest(MyController.class)
@Import(MySecurityConfiguration.class)
class MyControllerTests {
    // DataSource not loaded — test stays fast
}

Control test execution order

JUnit 5 allows you to specify the order in which test methods run within a class using @TestMethodOrder. Use this when tests share state that must be set up in sequence.
MyOrderedTests.java
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MyOrderedTests {

    @Test
    @Order(1)
    void createResource() {
    }

    @Test
    @Order(2)
    void updateResource() {
    }

    @Test
    @Order(3)
    void deleteResource() {
    }

}
Test ordering is most useful for integration tests that share a single database and represent a workflow. For unit tests, prefer independent, order-agnostic tests.

Enable parallel test execution

Running tests in parallel can significantly reduce total build time on multi-core machines. Configure JUnit 5’s parallel execution in a junit-platform.properties file:
src/test/resources/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.config.dynamic.factor=1
For @SpringBootTest tests that share an ApplicationContext, add @Isolated to any test class that must not run concurrently with others:
MySequentialTests.java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Isolated;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
@Isolated
class MySequentialTests {

    @Test
    void mustNotRunConcurrently() {
    }

}
Parallel execution requires that your tests are thread-safe. Tests that share mutable state (such as a database without transaction rollback) can produce flaky results when run concurrently. Validate each test class for thread safety before enabling parallel execution globally.

Build docs developers (and LLMs) love