引入spring-cloud-contract-wiremock后后续带@SqlGroup注解的测试执行失败
问题根因
该问题是Spring测试上下文缓存 + 数据库状态污染共同导致的:
- 你原有测试的
@SpringBootTest默认使用MOCK类型的web环境,新增的WireMock测试指定了RANDOM_PORT类型的Servlet web环境,两者属于不同的Spring上下文配置,Spring测试框架会分别缓存上下文 - 如果WireMock测试运行过程中修改了数据库数据,且没有做数据回滚/上下文清理,后续普通测试复用上下文或者访问同一个数据库时,就会因为已有脏数据导致
@Sql脚本执行失败(最常见的是主键冲突、表已存在等报错)
解决方案
你可以按优先级选择以下任意一种方案:
- 方案1(最通用、改动最小):给WireMock测试类添加
@DirtiesContext注解,指定类运行完后直接销毁当前上下文:
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @AutoConfigureWireMock(port = 10000, stubs = {"classpath:stubs/mappings"}) @ActiveProfiles({"test"}) public class YourWireMockTest { // 测试逻辑 }
该方案会强制跑完WireMock测试后销毁对应的Spring上下文,后续的普通测试会重新加载原本的非web环境上下文,同时如果使用的是内存型数据库(如H2),内存数据也会随上下文销毁被清空,彻底避免脏数据影响。
方案2(适合WireMock测试不需要操作数据库的场景):调整
@Sql脚本为幂等写法,避免重复执行报错:- 插入数据前先判断是否存在,比如MySQL可以用
INSERT IGNORE INTO、ON DUPLICATE KEY UPDATE,H2可以用MERGE INTO语法 - 在
@SqlGroup里增加测试前清理数据的脚本,先清空对应表再执行初始化语句
- 插入数据前先判断是否存在,比如MySQL可以用
方案3(适合所有测试需要复用同一个上下文的场景):统一所有测试的
@SpringBootTest配置,给原有普通测试也加上webEnvironment = SpringBootTest.WebEnvironment.MOCK显式声明,同时给所有需要修改数据库的测试类添加@Transactional注解,测试方法跑完后自动回滚事务(注意:RANDOM_PORT环境下@Transactional默认不生效,因为Servlet线程和测试线程事务不共享,所以该场景还是优先选方案1)
内容的提问来源于stack exchange,提问作者user3475366
相关产品推荐
相关产品推荐

