Skip to Content

集成测试规范

此规则定义了集成测试的编写规范,包括测试基类、MockMvc 配置、测试数据准备、断言风格等。它确保测试代码的质量和一致性。

适用范围: src/test/java/com/example/order/**/*.java

integration-test.mdc
--- description: 集成测试规范 globs: - src/test/java/com/example/order/**/*.java alwaysApply: false --- # 集成测试规范 ## 基础结构 所有集成测试必须继承 `BaseIntegrationTest` ```java public class OrderControllerTest extends BaseIntegrationTest { // ... } ``` ### BaseIntegrationTest 提供的能力 - `@SpringBootTest` - 完整 Spring 上下文 - `@Transactional` - 每个测试自动回滚 - `@ExtendWith(SpringExtension.class)` - JUnit 5 集成 ## 测试类组织 ### 使用 @Nested 按功能分组 ```java public class OrderControllerTest extends BaseIntegrationTest { @Nested class CreateOrder { @Test void should_return_400_given_invalid_customer_id() { } @Test void should_successfully_create_given_valid_request() { } } @Nested class UpdateOrder { @Test void should_successfully_update_given_valid_request() { } } } ``` ### 测试方法命名 格式:`should_{期望行为}_given_{前置条件}``should_{期望行为}_when_{触发条件}` ```java void should_return_400_given_invalid_customer_id() void should_successfully_create_given_valid_request() void should_throw_forbidden_error_given_user_has_no_permission() void should_return_order_list_when_filter_by_status() ``` ## MockMvc 设置 `@BeforeEach` 中初始化: ```java @Resource OrderController orderController; private MockMvc mockMvc; @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); this.mockMvc = MockMvcBuilders .standaloneSetup(orderController) .setControllerAdvice(ControllerAdvice.class) .build(); // 设置当前用户 SessionUserContext.setSessionUser( new SessionUser(1L, "user1", "user1@test.com", "10001")); } ``` ## 测试数据准备 ### SQL 脚本(准备数据库数据) 位置:`src/test/resources/sql/{module}/{scenario}/xxx.sql` ```java @Resource SqlHelper sqlHelper; @Test void should_successfully_create_given_valid_request() throws Exception { // given sqlHelper.run("sql/order/create-order/create-customer-with-address.sql"); // when & then // ... } @AfterEach public void clearData() { sqlHelper.run("sql/order/clear-data.sql"); } ``` ### JSON 文件(请求体数据) 位置:`src/test/resources/test-data/{module}/{scenario}/xxx.json` ```java // 读取为字符串(用于 RequestBody) String payload = JsonReader.readAsString("test-data/order/create-order/valid-order.json"); // 读取为对象(需要修改后使用) OrderUpdateDTO dto = JsonReader.readObject( "test-data/order/update-order/valid.json", OrderUpdateDTO.class ); dto.setId(order.getId()); ``` ## API 测试模式 ### 基本请求结构 ```java MvcResult result = mockMvc.perform( post("/api/order/create") .headers(AuthUtil.buildHeadersForUserId("10001")) .contentType(MediaType.APPLICATION_JSON) .content(payload)) .andExpect(status().isOk()) .andReturn(); ``` ### 解析响应 ```java // 单个对象 OrderInfoDTO dto = JsonUtil.readAsObject(result, OrderInfoDTO.class); // 列表 List<OrderItemDTO> list = JsonUtil.readAsList(result, OrderItemDTO.class); ``` ### 验证错误响应 ```java MvcResult result = mockMvc.perform(...) .andExpect(status().isBadRequest()) .andReturn(); ErrorVO errorVO = JsonUtil.readAsObject(result, ErrorVO.class); assertTrue(errorVO.getErrorMessage().startsWith("invalid customer id")); ``` ### 验证异常类型 ```java mockMvc.perform(...) .andExpect(result -> assertInstanceOf(BizException.class, result.getResolvedException())) .andExpect(result -> assertEquals("expected message", Objects.requireNonNull(result.getResolvedException()).getMessage())); ``` ## 断言风格 使用 AssertJ 链式断言: ```java assertThat(orderInfoDTO) .returns("PENDING", OrderInfoDTO::getStatus) .returns(order.getId(), OrderInfoDTO::getId) .returns(order.getCustomerId(), OrderInfoDTO::getCustomerId); assertThat(list) .hasSize(2) .extracting(OrderItemDTO::getProductName) .containsExactlyInAnyOrder("Product A", "Product B") .doesNotContain("Product C"); ``` ## 已封装的测试工具 | 工具类 | 路径 | 用途 | | ------------- | --------------------- | ---------------------- | | `SqlHelper` | `integration/helper/` | 执行 SQL 脚本 | | `JsonReader` | `integration/util/` | 读取 JSON 测试数据文件 | | `JsonUtil` | `integration/util/` | 解析 MvcResult 响应 | | `AuthUtil` | `integration/util/` | 构建认证 Header | | `OrderHelper` | `integration/helper/` | 构建订单相关测试实体 | ## Mock 静态方法(如 LocalDate.now()) ```java try (MockedStatic<LocalDate> localDate = mockStatic(LocalDate.class, CALLS_REAL_METHODS)) { localDate.when(LocalDate::now).thenReturn(LocalDate.of(2024, 4, 9)); // 执行测试 mockMvc.perform(...); } ```
最后更新于: