spring-boot-test-patterns

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Spring Boot Testing Patterns

Spring Boot 测试模式

Overview

概述

This skill provides comprehensive guidance for writing robust test suites for Spring Boot applications. It covers unit testing with Mockito, integration testing with Testcontainers, performance-optimized slice testing patterns, and best practices for maintaining fast feedback loops.
本技能为编写Spring Boot应用的健壮测试套件提供全面指导,涵盖使用Mockito进行单元测试、使用Testcontainers进行集成测试、性能优化的切片测试模式,以及维护快速反馈循环的最佳实践。

When to Use This Skill

适用场景

Use this skill when:
  • Writing unit tests for services, repositories, or utilities
  • Implementing integration tests with real databases using Testcontainers
  • Setting up performance-optimized test slices (@DataJpaTest, @WebMvcTest)
  • Configuring Spring Boot 3.5+ @ServiceConnection for container management
  • Testing REST APIs with MockMvc, TestRestTemplate, or WebTestClient
  • Optimizing test performance through context caching and container reuse
  • Setting up CI/CD pipelines for integration tests
  • Implementing comprehensive test strategies for monolithic or microservices applications
在以下场景中使用本技能:
  • 为服务、仓库或工具类编写单元测试
  • 使用Testcontainers结合真实数据库实现集成测试
  • 设置性能优化的测试切片(@DataJpaTest、@WebMvcTest)
  • 配置Spring Boot 3.5+的@ServiceConnection进行容器管理
  • 使用MockMvc、TestRestTemplate或WebTestClient测试REST API
  • 通过上下文缓存和容器复用优化测试性能
  • 为集成测试设置CI/CD流水线
  • 为单体或微服务应用实现全面的测试策略

Core Concepts

核心概念

Test Architecture Philosophy

测试架构理念

Spring Boot testing follows a layered approach with distinct test types:
1. Unit Tests
  • Fast, isolated tests without Spring context
  • Use Mockito for dependency injection
  • Focus on business logic validation
  • Target completion time: < 50ms per test
2. Slice Tests
  • Minimal Spring context loading for specific layers
  • Use @DataJpaTest for repository tests
  • Use @WebMvcTest for controller tests
  • Use @WebFluxTest for reactive controller tests
  • Target completion time: < 100ms per test
3. Integration Tests
  • Full Spring context with real dependencies
  • Use @SpringBootTest with @ServiceConnection containers
  • Test complete application flows
  • Target completion time: < 500ms per test
Spring Boot测试遵循分层方法,包含不同的测试类型:
1. 单元测试
  • 快速、隔离的测试,无需加载Spring上下文
  • 使用Mockito进行依赖注入
  • 专注于业务逻辑验证
  • 目标完成时间:每个测试<50ms
2. 切片测试
  • 加载特定层的最小Spring上下文
  • 使用@DataJpaTest进行仓库测试
  • 使用@WebMvcTest进行控制器测试
  • 使用@WebFluxTest进行响应式控制器测试
  • 目标完成时间:每个测试<100ms
3. 集成测试
  • 加载完整Spring上下文并使用真实依赖
  • 结合@SpringBootTest与@ServiceConnection容器
  • 测试完整的应用流程
  • 目标完成时间:每个测试<500ms

Key Testing Annotations

关键测试注解

Spring Boot Test Annotations:
  • @SpringBootTest
    : Load full application context (use sparingly)
  • @DataJpaTest
    : Load only JPA components (repositories, entities)
  • @WebMvcTest
    : Load only MVC layer (controllers, @ControllerAdvice)
  • @WebFluxTest
    : Load only WebFlux layer (reactive controllers)
  • @JsonTest
    : Load only JSON serialization components
Testcontainer Annotations:
  • @ServiceConnection
    : Wire Testcontainer to Spring Boot test (Spring Boot 3.5+)
  • @DynamicPropertySource
    : Register dynamic properties at runtime
  • @Testcontainers
    : Enable Testcontainers lifecycle management
Spring Boot测试注解:
  • @SpringBootTest
    :加载完整应用上下文(谨慎使用)
  • @DataJpaTest
    :仅加载JPA组件(仓库、实体)
  • @WebMvcTest
    :仅加载MVC层(控制器、@ControllerAdvice)
  • @WebFluxTest
    :仅加载WebFlux层(响应式控制器)
  • @JsonTest
    :仅加载JSON序列化组件
Testcontainers注解:
  • @ServiceConnection
    :将Testcontainer与Spring Boot测试关联(Spring Boot 3.5+)
  • @DynamicPropertySource
    :在运行时注册动态属性
  • @Testcontainers
    :启用Testcontainers生命周期管理

Dependencies

依赖配置

Maven Dependencies

Maven依赖

xml
<dependencies>
    <!-- Spring Boot Test Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- Testcontainers -->
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>1.19.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>1.19.0</version>
        <scope>test</scope>
    </dependency>

    <!-- Additional Testing Dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
xml
<dependencies>
    <!-- Spring Boot Test Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- Testcontainers -->
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>1.19.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>1.19.0</version>
        <scope>test</scope>
    </dependency>

    <!-- Additional Testing Dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

Gradle Dependencies

Gradle依赖

kotlin
dependencies {
    // Spring Boot Test Starter
    testImplementation("org.springframework.boot:spring-boot-starter-test")

    // Testcontainers
    testImplementation("org.testcontainers:junit-jupiter:1.19.0")
    testImplementation("org.testcontainers:postgresql:1.19.0")

    // Additional Dependencies
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
}
kotlin
dependencies {
    // Spring Boot Test Starter
    testImplementation("org.springframework.boot:spring-boot-starter-test")

    // Testcontainers
    testImplementation("org.testcontainers:junit-jupiter:1.19.0")
    testImplementation("org.testcontainers:postgresql:1.19.0")

    // Additional Dependencies
    implementation("org.springframework.boot:spring-boot-starter-data-jpa")
    implementation("org.springframework.boot:spring-boot-starter-web")
}

Instructions

操作指南

Unit Testing Pattern

单元测试模式

Test business logic with mocked dependencies:
java
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void shouldFindUserByIdWhenExists() {
        // Arrange
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setEmail("test@example.com");

        when(userRepository.findById(userId)).thenReturn(Optional.of(user));

        // Act
        Optional<User> result = userService.findById(userId);

        // Assert
        assertThat(result).isPresent();
        assertThat(result.get().getEmail()).isEqualTo("test@example.com");
        verify(userRepository, times(1)).findById(userId);
    }
}
使用模拟依赖测试业务逻辑:
java
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void shouldFindUserByIdWhenExists() {
        // Arrange
        Long userId = 1L;
        User user = new User();
        user.setId(userId);
        user.setEmail("test@example.com");

        when(userRepository.findById(userId)).thenReturn(Optional.of(user));

        // Act
        Optional<User> result = userService.findById(userId);

        // Assert
        assertThat(result).isPresent();
        assertThat(result.get().getEmail()).isEqualTo("test@example.com");
        verify(userRepository, times(1)).findById(userId);
    }
}

Slice Testing Pattern

切片测试模式

Use focused test slices for specific layers:
java
// Repository test with minimal context
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
public class UserRepositoryIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldSaveAndRetrieveUserFromDatabase() {
        // Arrange
        User user = new User();
        user.setEmail("test@example.com");
        user.setName("Test User");

        // Act
        User saved = userRepository.save(user);
        userRepository.flush();

        Optional<User> retrieved = userRepository.findByEmail("test@example.com");

        // Assert
        assertThat(retrieved).isPresent();
        assertThat(retrieved.get().getName()).isEqualTo("Test User");
    }
}
针对特定层使用聚焦的测试切片:
java
// Repository test with minimal context
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
public class UserRepositoryIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldSaveAndRetrieveUserFromDatabase() {
        // Arrange
        User user = new User();
        user.setEmail("test@example.com");
        user.setName("Test User");

        // Act
        User saved = userRepository.save(user);
        userRepository.flush();

        Optional<User> retrieved = userRepository.findByEmail("test@example.com");

        // Assert
        assertThat(retrieved).isPresent();
        assertThat(retrieved.get().getName()).isEqualTo("Test User");
    }
}

REST API Testing Pattern

REST API测试模式

Test controllers with MockMvc for faster execution:
java
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class UserControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserService userService;

    @Test
    void shouldCreateUserAndReturn201() throws Exception {
        User user = new User();
        user.setEmail("newuser@example.com");
        user.setName("New User");

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").exists())
            .andExpect(jsonPath("$.email").value("newuser@example.com"))
            .andExpect(jsonPath("$.name").value("New User"));
    }
}
使用MockMvc测试控制器以提升执行速度:
java
@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class UserControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private UserService userService;

    @Test
    void shouldCreateUserAndReturn201() throws Exception {
        User user = new User();
        user.setEmail("newuser@example.com");
        user.setName("New User");

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(user)))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").exists())
            .andExpect(jsonPath("$.email").value("newuser@example.com"))
            .andExpect(jsonPath("$.name").value("New User"));
    }
}

Testcontainers with @ServiceConnection

结合@ServiceConnection使用Testcontainers

Configure containers with Spring Boot 3.5+:
java
@TestConfiguration
public class TestContainerConfig {

    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"))
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");
    }
}
配置Spring Boot 3.5+的容器:
java
@TestConfiguration
public class TestContainerConfig {

    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"))
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");
    }
}

Examples

示例

Basic Unit Test

基础单元测试

java
@Test
void shouldCalculateTotalPrice() {
    // Arrange
    OrderItem item1 = new OrderItem();
    item1.setPrice(10.0);
    item1.setQuantity(2);

    OrderItem item2 = new OrderItem();
    item2.setPrice(15.0);
    item2.setQuantity(1);

    List<OrderItem> items = List.of(item1, item2);

    // Act
    double total = orderService.calculateTotal(items);

    // Assert
    assertThat(total).isEqualTo(35.0);
}
java
@Test
void shouldCalculateTotalPrice() {
    // Arrange
    OrderItem item1 = new OrderItem();
    item1.setPrice(10.0);
    item1.setQuantity(2);

    OrderItem item2 = new OrderItem();
    item2.setPrice(15.0);
    item2.setQuantity(1);

    List<OrderItem> items = List.of(item1, item2);

    // Act
    double total = orderService.calculateTotal(items);

    // Assert
    assertThat(total).isEqualTo(35.0);
}

Integration Test with Testcontainers

结合Testcontainers的集成测试

java
@SpringBootTest
@TestContainerConfig
public class OrderServiceIntegrationTest {

    @Autowired
    private OrderService orderService;

    @Autowired
    private UserRepository userRepository;

    @MockBean
    private PaymentService paymentService;

    @Test
    void shouldCreateOrderWithRealDatabase() {
        // Arrange
        User user = new User();
        user.setEmail("customer@example.com");
        user.setName("John Doe");
        User savedUser = userRepository.save(user);

        OrderRequest request = new OrderRequest();
        request.setUserId(savedUser.getId());
        request.setItems(List.of(
            new OrderItemRequest(1L, 2),
            new OrderItemRequest(2L, 1)
        ));

        when(paymentService.processPayment(any())).thenReturn(true);

        // Act
        OrderResponse response = orderService.createOrder(request);

        // Assert
        assertThat(response.getOrderId()).isNotNull();
        assertThat(response.getStatus()).isEqualTo("COMPLETED");
        verify(paymentService, times(1)).processPayment(any());
    }
}
java
@SpringBootTest
@TestContainerConfig
public class OrderServiceIntegrationTest {

    @Autowired
    private OrderService orderService;

    @Autowired
    private UserRepository userRepository;

    @MockBean
    private PaymentService paymentService;

    @Test
    void shouldCreateOrderWithRealDatabase() {
        // Arrange
        User user = new User();
        user.setEmail("customer@example.com");
        user.setName("John Doe");
        User savedUser = userRepository.save(user);

        OrderRequest request = new OrderRequest();
        request.setUserId(savedUser.getId());
        request.setItems(List.of(
            new OrderItemRequest(1L, 2),
            new OrderItemRequest(2L, 1)
        ));

        when(paymentService.processPayment(any())).thenReturn(true);

        // Act
        OrderResponse response = orderService.createOrder(request);

        // Assert
        assertThat(response.getOrderId()).isNotNull();
        assertThat(response.getStatus()).isEqualTo("COMPLETED");
        verify(paymentService, times(1)).processPayment(any());
    }
}

Reactive Test Pattern

响应式测试模式

java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class ReactiveUserControllerIntegrationTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void shouldReturnUserAsJsonReactive() {
        // Arrange
        User user = new User();
        user.setEmail("reactive@example.com");
        user.setName("Reactive User");

        // Act & Assert
        webTestClient.get()
            .uri("/api/users/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody()
            .jsonPath("$.email").isEqualTo("reactive@example.com")
            .jsonPath("$.name").isEqualTo("Reactive User");
    }
}
java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWebTestClient
public class ReactiveUserControllerIntegrationTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void shouldReturnUserAsJsonReactive() {
        // Arrange
        User user = new User();
        user.setEmail("reactive@example.com");
        user.setName("Reactive User");

        // Act & Assert
        webTestClient.get()
            .uri("/api/users/1")
            .exchange()
            .expectStatus().isOk()
            .expectBody()
            .jsonPath("$.email").isEqualTo("reactive@example.com")
            .jsonPath("$.name").isEqualTo("Reactive User");
    }
}

Best Practices

最佳实践

1. Choose the Right Test Type

1. 选择合适的测试类型

Select appropriate test annotations based on scope:
java
// Use @DataJpaTest for repository-only tests (fastest)
@DataJpaTest
public class UserRepositoryTest { }

// Use @WebMvcTest for controller-only tests
@WebMvcTest(UserController.class)
public class UserControllerTest { }

// Use @SpringBootTest only for full integration testing
@SpringBootTest
public class UserServiceFullIntegrationTest { }
根据测试范围选择合适的测试注解:
java
// Use @DataJpaTest for repository-only tests (fastest)
@DataJpaTest
public class UserRepositoryTest { }

// Use @WebMvcTest for controller-only tests
@WebMvcTest(UserController.class)
public class UserControllerTest { }

// Use @SpringBootTest only for full integration testing
@SpringBootTest
public class UserServiceFullIntegrationTest { }

2. Use @ServiceConnection for Container Management

2. 使用@ServiceConnection进行容器管理

Prefer
@ServiceConnection
over manual
@DynamicPropertySource
for cleaner code:
java
// Good - Spring Boot 3.5+
@TestConfiguration
public class TestConfig {
    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgres() {
        return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"));
    }
}
优先使用
@ServiceConnection
而非手动配置
@DynamicPropertySource
,使代码更简洁:
java
// Good - Spring Boot 3.5+
@TestConfiguration
public class TestConfig {
    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgres() {
        return new PostgreSQLContainer<>(DockerImageName.parse("postgres:16-alpine"));
    }
}

3. Keep Tests Deterministic

3. 保持测试的确定性

Always initialize test data explicitly:
java
// Good - Explicit setup
@BeforeEach
void setUp() {
    userRepository.deleteAll();
    User user = new User();
    user.setEmail("test@example.com");
    userRepository.save(user);
}

// Avoid - Depending on other tests
@Test
void testUserExists() {
    // Assumes previous test created a user
    Optional<User> user = userRepository.findByEmail("test@example.com");
    assertThat(user).isPresent();
}
始终显式初始化测试数据:
java
// Good - Explicit setup
@BeforeEach
void setUp() {
    userRepository.deleteAll();
    User user = new User();
    user.setEmail("test@example.com");
    userRepository.save(user);
}

// Avoid - Depending on other tests
@Test
void testUserExists() {
    // Assumes previous test created a user
    Optional<User> user = userRepository.findByEmail("test@example.com");
    assertThat(user).isPresent();
}

4. Use Meaningful Assertions

4. 使用有意义的断言

Leverage AssertJ for readable, fluent assertions:
java
// Good - Clear, readable assertions
assertThat(user.getEmail())
    .isEqualTo("test@example.com");

assertThat(users)
    .hasSize(3)
    .contains(expectedUser);

// Avoid - JUnit assertions
assertEquals("test@example.com", user.getEmail());
assertTrue(users.size() == 3);
利用AssertJ实现可读性强的流畅断言:
java
// Good - Clear, readable assertions
assertThat(user.getEmail())
    .isEqualTo("test@example.com");

assertThat(users)
    .hasSize(3)
    .contains(expectedUser);

// Avoid - JUnit assertions
assertEquals("test@example.com", user.getEmail());
assertTrue(users.size() == 3);

5. Organize Tests by Layer

5. 按层组织测试

Group related tests in separate classes to optimize context caching:
java
// Repository tests (uses @DataJpaTest)
public class UserRepositoryTest { }

// Controller tests (uses @WebMvcTest)
public class UserControllerTest { }

// Service tests (uses mocks, no context)
public class UserServiceTest { }

// Full integration tests (uses @SpringBootTest)
public class UserFullIntegrationTest { }
将相关测试分组到不同类中,优化上下文缓存:
java
// Repository tests (uses @DataJpaTest)
public class UserRepositoryTest { }

// Controller tests (uses @WebMvcTest)
public class UserControllerTest { }

// Service tests (uses mocks, no context)
public class UserServiceTest { }

// Full integration tests (uses @SpringBootTest)
public class UserFullIntegrationTest { }

Performance Optimization

性能优化

Context Caching Strategy

上下文缓存策略

Maximize Spring context caching by grouping tests with similar configurations:
java
// Group repository tests with same configuration
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
@TestPropertySource(properties = "spring.datasource.url=jdbc:postgresql:testdb")
public class UserRepositoryTest { }

// Group controller tests with same configuration
@WebMvcTest(UserController.class)
@AutoConfigureMockMvc
public class UserControllerTest { }
通过将具有相同配置的测试分组,最大化Spring上下文缓存:
java
// Group repository tests with same configuration
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestContainerConfig
@TestPropertySource(properties = "spring.datasource.url=jdbc:postgresql:testdb")
public class UserRepositoryTest { }

// Group controller tests with same configuration
@WebMvcTest(UserController.class)
@AutoConfigureMockMvc
public class UserControllerTest { }

Container Reuse Strategy

容器复用策略

Reuse Testcontainers at JVM level for better performance:
java
@Testcontainers
public class ContainerConfig {
    static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
        DockerImageName.parse("postgres:16-alpine"))
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");

    @BeforeAll
    static void startAll() {
        POSTGRES.start();
    }

    @AfterAll
    static void stopAll() {
        POSTGRES.stop();
    }
}
在JVM级别复用Testcontainers以提升性能:
java
@Testcontainers
public class ContainerConfig {
    static final PostgreSQLContainer<?> POSTGRES = new PostgreSQLContainer<>(
        DockerImageName.parse("postgres:16-alpine"))
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");

    @BeforeAll
    static void startAll() {
        POSTGRES.start();
    }

    @AfterAll
    static void stopAll() {
        POSTGRES.stop();
    }
}

Test Execution

测试执行

Maven Test Execution

Maven测试执行

bash
undefined
bash
undefined

Run all tests

Run all tests

./mvnw test
./mvnw test

Run specific test class

Run specific test class

./mvnw test -Dtest=UserServiceTest
./mvnw test -Dtest=UserServiceTest

Run integration tests only

Run integration tests only

./mvnw test -Dintegration-test=true
./mvnw test -Dintegration-test=true

Run tests with coverage

Run tests with coverage

./mvnw clean jacoco:prepare-agent test jacoco:report
undefined
./mvnw clean jacoco:prepare-agent test jacoco:report
undefined

Gradle Test Execution

Gradle测试执行

bash
undefined
bash
undefined

Run all tests

Run all tests

./gradlew test
./gradlew test

Run specific test class

Run specific test class

./gradlew test --tests UserServiceTest
./gradlew test --tests UserServiceTest

Run integration tests only

Run integration tests only

./gradlew integrationTest
./gradlew integrationTest

Run tests with coverage

Run tests with coverage

./gradlew test jacocoTestReport
undefined
./gradlew test jacocoTestReport
undefined

CI/CD Configuration

CI/CD配置

GitHub Actions Example

GitHub Actions示例

yaml
name: Spring Boot Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_USER: test
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v3

    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2/repository
        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-maven-

    - name: Run tests
      run: ./mvnw test -Dspring.profiles.active=test
yaml
name: Spring Boot Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_PASSWORD: test
          POSTGRES_USER: test
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v3

    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Cache Maven dependencies
      uses: actions/cache@v3
      with:
        path: ~/.m2/repository
        key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
        restore-keys: ${{ runner.os }}-maven-

    - name: Run tests
      run: ./mvnw test -Dspring.profiles.active=test

Docker Compose for Local Testing

Docker Compose本地测试

yaml
version: '3.8'
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
yaml
version: '3.8'
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: testdb
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

References

参考资料

For detailed information, refer to the following resources:
  • API Reference - Complete test annotations and utilities
  • Best Practices - Testing patterns and optimization
  • Workflow Patterns - Complete integration test examples
如需详细信息,请参考以下资源:
  • API参考 - 完整的测试注解和工具类
  • 最佳实践 - 测试模式与优化
  • 工作流模式 - 完整的集成测试示例

Related Skills

相关技能

  • spring-boot-dependency-injection - Unit testing patterns with constructor injection
  • spring-boot-rest-api-standards - REST API patterns to test
  • spring-boot-crud-patterns - CRUD patterns to test
  • unit-test-service-layer - Advanced service layer testing techniques
  • spring-boot-dependency-injection - 结合构造注入的单元测试模式
  • spring-boot-rest-api-standards - 待测试的REST API模式
  • spring-boot-crud-patterns - 待测试的CRUD模式
  • unit-test-service-layer - 高级服务层测试技巧

Performance Targets

性能目标

  • Unit tests: < 50ms per test
  • Slice tests: < 100ms per test
  • Integration tests: < 500ms per test
  • Maximize context caching by grouping tests with same configuration
  • Reuse Testcontainers at JVM level where possible
  • 单元测试:每个测试<50ms
  • 切片测试:每个测试<100ms
  • 集成测试:每个测试<500ms
  • 通过将相同配置的测试分组,最大化上下文缓存
  • 尽可能在JVM级别复用Testcontainers

Key Principles

核心原则

  1. Use test slices for focused, fast tests
  2. Prefer @ServiceConnection on Spring Boot 3.5+
  3. Keep tests deterministic with explicit setup
  4. Mock external dependencies, use real databases
  5. Avoid @DirtiesContext unless absolutely necessary
  6. Organize tests by layer to optimize context reuse
This skill enables building comprehensive test suites that validate Spring Boot applications reliably while maintaining fast feedback loops for development.
  1. 使用测试切片实现聚焦、快速的测试
  2. 在Spring Boot 3.5+中优先使用@ServiceConnection
  3. 通过显式设置保持测试的确定性
  4. 模拟外部依赖,使用真实数据库
  5. 除非绝对必要,否则避免使用@DirtiesContext
  6. 按层组织测试以优化上下文复用
本技能支持构建全面的测试套件,可靠地验证Spring Boot应用,同时为开发过程维护快速的反馈循环。