AgentSkillsCN

spring-testing

利用 @SpringBootTest、MockMvc 与 Testcontainers 进行 Spring Boot 测试

SKILL.md
--- frontmatter
name: spring-testing
description: Spring Boot testing with @SpringBootTest, MockMvc, and Testcontainers
keywords:
  - spring test
  - springboot test
  - mockmvc
  - testcontainers
  - integration test
  - slice test
  - @SpringBootTest
  - @WebMvcTest
  - @DataJpaTest
filePatterns:
  - "*.java"
  - "application.yml"
  - "application.properties"
frameworks:
  - spring-boot
  - junit
  - testcontainers
tokenCount: 2800
version: 1.0.0

Spring Boot Testing Patterns

Reliable tests for Spring Boot services and APIs. Prefer slice tests for speed, full context when needed.

Selective Reading Rule

Read only files relevant to the request. Use the content map to focus.

Content Map

FileDescriptionWhen to Read
slice-tests.md@WebMvcTest, @DataJpaTestFast feedback
integration.md@SpringBootTest, TestcontainersReal dependencies
mockmvc.mdMockMvc usageController tests
test-data.mdTest fixtures, buildersClean data setup

Core Patterns

1. Web Slice Tests

java
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Test
    void getUser_returnsUser() throws Exception {
        when(userService.findById("u1"))
            .thenReturn(Optional.of(new User("u1", "dev@example.com")));

        mockMvc.perform(get("/api/v1/users/u1"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.email").value("dev@example.com"));
    }
}

2. Data JPA Slice

java
@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void findByEmail_returnsUser() {
        var user = userRepository.save(new User("u1", "dev@example.com"));
        assertThat(userRepository.findByEmail("dev@example.com")).isPresent();
    }
}

3. Full Integration Test

java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
class UserControllerIT {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void createUser_returnsCreated() {
        var request = new CreateUserRequest("dev@example.com", "Dev");
        var response = restTemplate.postForEntity("/api/v1/users", request, UserResponse.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
    }
}

Decision Checklist

  • Slice test for controller/repo?
  • Use MockMvc over full context?
  • Testcontainers for real DB?
  • Avoid shared state between tests?

Anti-Patterns

Anti-PatternWhy BadBetter Approach
@SpringBootTest for all testsSlowSlice tests
Real DB without isolationFlakyTestcontainers
Static mutable fixturesHidden couplingBuilders/per-test setup

Related Skills

NeedSkill
Core Spring patterns@[skills/spring-boot-patterns]
Data access@[skills/spring-data-jpa]