如何实现JUnit中@Sql脚本与测试操作的回滚,避免测试间相互影响?
这个问题我之前也碰到过,确实挺头疼的——JUnit默认的测试执行顺序是按方法名哈希值排序的(看起来像是逆序),再加上@Sql脚本的操作没有自动回滚,直接导致了测试数据互相污染。给你几个靠谱的解决办法:
1. 用@Transactional实现自动回滚
这是最推荐的方案,Spring Test原生支持在测试类或方法上添加@Transactional注解:它会在每个测试方法执行前开启事务,测试结束后自动回滚所有操作——包括@Sql脚本执行的数据库变更,以及测试方法自身的增删改操作。
修改你的测试类如下:
@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @Transactional // 新增这个注解,所有测试方法都会自动回滚 public class MyTest { @Sql("test_insert.sql") @Test public void testInsert(){ // ...执行一些插入操作... } @Sql("test_delete.sql") @Test public void testDelete(){ // ... } }
不管测试顺序怎么变,每个测试执行完都会把数据库恢复到初始状态,完全不会互相影响。注意:如果你的测试方法里手动调用了transactionManager.commit(),那回滚就会失效,要避免这种操作。
2. 显式控制测试执行顺序
如果你不想用事务回滚(比如某些场景下需要持久化测试数据),可以强制指定测试方法的执行顺序。用@FixMethodOrder注解,指定按方法名的字母顺序执行:
@RunWith(SpringRunner.class) @SpringBootTest @ActiveProfiles("test") @FixMethodOrder(MethodSorters.NAME_ASCENDING) // 按方法名升序执行 public class MyTest { // ... 测试方法保持不变 ... }
这样testDelete就不会在testInsert之前执行了,但这个方案不如回滚可靠——如果某个测试方法抛出异常导致数据没清理,还是会影响后续测试。
3. 调整@Sql的执行时机
针对你的testDelete方法,如果它的逻辑是先插入测试数据再执行删除,你可以把插入脚本的执行时机改成测试前,清理脚本改成测试后,用executionPhase属性:
@Sql(scripts = "test_insert_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) @Sql(scripts = "test_cleanup.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @Test public void testDelete(){ // ... 执行删除操作 ... }
这样每个测试的前置数据只在当前测试生命周期内存在,测试结束就清理,但还是不如@Transactional省心,毕竟要维护额外的清理脚本。
总结一下,优先用@Transactional,它是Spring Test解决测试数据污染的标准方案,简单又可靠。
内容的提问来源于stack exchange,提问作者Phate

