You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:47:16