如何配置MockMvc确保Spring Boot集成测试互不干扰
你的问题不是MockMvc没有重置,而是测试之间的数据库状态没有隔离。Spring Boot集成测试默认会复用应用上下文,导致前一个测试操作的数据库数据(比如添加的员工、单元、层级)会残留到下一个测试中,从而干扰测试结果。MockMvc本身是线程安全的,不需要手动重置,核心解决思路是隔离每个测试的数据库状态。
1. 用事务自动回滚隔离测试数据
给测试类添加@Transactional注解,Spring会为每个测试方法创建独立的事务,测试结束后自动回滚,确保数据库回到初始状态:
@SpringBootTest @AutoConfigureMockMvc @Transactional // 新增:自动回滚每个测试的数据库操作 public class EmployeeControllerIntegrationTest { // 修正类名拼写:Controllor -> Controller // ... 原有代码 }
2. 优化依赖注入方式(符合Spring测试规范)
虽然构造函数注入合法,但Spring Boot测试中更常用@Autowired注入(两种方式都能拿到正确的MockMvc实例,选其一即可):
@SpringBootTest @AutoConfigureMockMvc @Transactional public class EmployeeControllerIntegrationTest { private final static String URI = "/employees/"; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; // 删除原构造函数,依赖由Spring自动注入 // ... 原有测试方法 }
3. 避免重复依赖数据插入的冲突
你的insertUnitAndLevel方法每次调用都会插入单元和层级,即使事务回滚,也可能因为接口重复校验报错。可以直接通过Repository操作数据库(更高效,避免调用Controller接口),并先检查数据是否存在:
// 注入对应的Repository(需要你提前定义) @Autowired private UnitRepository unitRepository; @Autowired private EnumerationLevelRepository levelRepository; private void insertUnitAndLevel() { // 不存在则插入单元 unitRepository.findByName("new unit") .orElseGet(() -> unitRepository.save(new Unit("new unit"))); // 不存在则插入层级 levelRepository.findByCode("A1") .orElseGet(() -> levelRepository.save(new EnumerationLevel("A1", "Mockdescripition"))); }
4. 可选:强制重置应用上下文(仅作为最后手段)
如果事务回滚无法解决问题(比如涉及非事务性资源),可以用@DirtiesContext强制每个测试后重建上下文,但会大幅增加测试耗时:
@Test @DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD) void getAllEmployeesThrowsExceptionWhenEmpty() throws Exception { // ... 测试代码 }
修改后的完整测试类示例
@SpringBootTest @AutoConfigureMockMvc @Transactional public class EmployeeControllerIntegrationTest { private final static String URI = "/employees/"; @Autowired private ObjectMapper objectMapper; @Autowired private MockMvc mockMvc; @Autowired private UnitRepository unitRepository; @Autowired private EnumerationLevelRepository levelRepository; @Test void getAllEmployeesThrowsExceptionWhenEmpty() throws Exception{ String error = mockMvc.perform(get(URI+"showAll")) .andExpect(status().isAccepted()) .andExpect(content().contentType("application/json")) .andReturn().getResolvedException().getMessage(); assertThat(error).isEqualTo("No employees present"); } @Test void getAllEmployeesWorksWhenEmployeesPresent() throws Exception{ this.insertUnitAndLevel(); EmployeeDto employeeDto = new EmployeeDto("employeeName", "employeeNameSurname", LocalDate.of(2000,04,23),1L,1L); mockMvc.perform(post(URI+"addEmployee") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(employeeDto))); MockHttpServletResponse response = mockMvc.perform(get(URI+"showAll")) .andExpect(status().isOk()) .andExpect(content().contentType("application/json")) .andReturn().getResponse(); Employee[] allEmployees = objectMapper.readValue(response.getContentAsString(), Employee[].class); assertThat(allEmployees).hasSize(1); assertThat(allEmployees[0].getName()).isEqualTo("employeeName"); assertThat(allEmployees[0].getStartDateContract()).isEqualTo(LocalDate.now()); } // 其他测试方法保持不变... private void insertUnitAndLevel() { unitRepository.findByName("new unit") .orElseGet(() -> unitRepository.save(new Unit("new unit"))); levelRepository.findByCode("A1") .orElseGet(() -> levelRepository.save(new EnumerationLevel("A1", "Mockdescripition"))); } }
内容的提问来源于stack exchange,提问作者User69
相关产品推荐
相关产品推荐

