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

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上下文,只需要在测试清理阶段补全两步操作即可:

  1. 清理MongoDB中全部JaVers相关集合,不要遗漏提交记录相关表
  2. 主动调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 13:45:31