Spring Boot MongoDB测试中JaVers数据正确清理方案
问题背景
在集成MongoDB的Spring Boot应用中使用JaVers时,框架自动配置运行正常、使用简便,但测试阶段重复使用固定对象ID时会出现测试失败问题。
测试初始化阶段原本执行的JaVers集合清理代码如下:
mongoTemplate.db.getCollection("jv_snapshots").deleteMany(new Document()) mongoTemplate.db.getCollection("jv_head_id").deleteMany(new Document())
为简化测试对象创建流程,多个测试用例使用相同的固定对象ID,执行测试套件时第二个用例始终失败,对应的测试代码如下:
/** * [Test worker] INFO org.javers.core.Javers - Commit(id:1.00, snapshots:1, author:unauthenticated, changes - NewObject:1), done in 101 millis (diff:49, persist:52) */ @Order(0) def "create entity to induce error on second test"() { def entity = repo.save(dummyEntityWithFixedId()) def changes = javers.findChanges(QueryBuilder.byInstance(entity).build()) expect: changes.size() == 1 } /** * [Test worker] INFO org.javers.core.Javers - Skipping persisting empty commit: Commit(id:1.01, snapshots:0, author:unauthenticated, changes -) * [Test worker] INFO org.javers.core.Javers - Commit(id:1.01, snapshots:0, author:unauthenticated, changes -), done in 4 millis (diff:4, persist:0) */ @Order(1) def "fails on all test run because previous test is not cleaned correctly"() { def entity = repo.save(dummyEntityWithFixedId()) def changes = javers.findChanges(QueryBuilder.byInstance(entity).build()) expect: changes.size() == 1 }
问题表现
从日志可以看到,第二个测试运行时JaVers判定没有产生实际变更,跳过了持久化操作,最终返回的变更数量为0,不符合测试预期。
目前已知两种临时规避方案:
- 测试用例不使用固定实体ID,每次生成随机ID
- 给测试类/方法添加
@DirtiesContext注解,强制测试间刷新Spring上下文
但第一种方案会增加测试代码编写成本,第二种方案上下文重建开销极大,都不是最优解。
根因分析
问题核心是清理逻辑不完整:仅删除了MongoDB中持久化的JaVers数据,没有清空JaVers进程内的对象快照缓存。
JaVers为了提升diff计算性能,会在JVM内存中缓存已经处理过的对象快照、提交ID序列状态。即使数据库侧的集合被清空,内存中仍然保留了上一个测试用例提交的对象状态,第二次保存相同ID的对象时,JaVers对比内存中的旧快照认为没有字段变更,就会触发空提交跳过逻辑,返回0条变更。
轻量解决方案
不需要修改测试用例逻辑,也不需要重建Spring上下文,只需要在测试清理阶段补全两步操作即可:
- 清理MongoDB中全部JaVers相关集合,不要遗漏提交记录相关表
- 主动调用JaVers提供的方法清空内存缓存
具体实现代码如下(以Groovy/Java Spring Boot场景为例):
import org.javers.core.Javers import org.javers.repository.api.JaversExtendedRepository import org.springframework.data.mongodb.core.MongoTemplate import org.bson.Document import org.junit.jupiter.api.AfterEach import javax.annotation.Resource class JaversTest { @Resource Javers javers @Resource MongoTemplate mongoTemplate @AfterEach void cleanJaversState() { // 清理MongoDB侧所有JaVers集合 ["jv_snapshots", "jv_head_id", "jv_commit", "jv_commit_property"].each { mongoTemplate.getCollection(it).deleteMany(new Document()) } // 清空JaVers进程内缓存 JaversExtendedRepository javersRepo = (JaversExtendedRepository) javers.getRepository() javersRepo.clear() } // 原有测试方法保持不变,固定ID可以正常使用 }
该方案清理逻辑执行耗时在毫秒级,对测试运行速度几乎没有影响,也不需要改动原有测试用例的编写逻辑。
内容的提问来源于stack exchange,提问作者cgo
相关产品推荐
相关产品推荐

