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

多线程场景下Spock Mock时钟更新不生效的问题求助

解决Spock测试中跨线程Mock时钟不更新的问题

你的问题核心在于:Spock中对MyClock的Mock存根是追加式而非覆盖式的,且被测线程的忙循环会持续复用最初的存根返回值,导致后续更新的时间无法被感知。以下是几种可行的解决方案:

方案1:用动态闭包返回最新时间值

放弃每次静态设置>> mockedTime,改为让Mock的getTime()方法每次调用都返回当前的mockedTime变量值,利用Groovy闭包实现动态返回:

def 'test thread with dynamic clock mock' () {
    given:
    MyClock clockMock = Mock(MyClock)
    long mockedTime = 1L
    // 让getTime()每次调用都返回当前的mockedTime
    clockMock.getTime() >> { mockedTime }
    ClassUnderTest classUnderTest = new ClassUnderTest(clockMock)

    expect:
    for (int i = 0; i < 100; i++) {
        mockedTime += 1000 * 60 * 60 * 3 // 增加3小时
        // 给被测线程留出处理时间(根据实际逻辑调整等待时长)
        sleep(100)
        classUnderTest.getResult() == calculateExpectedValue(i)
    }

    cleanup:
    // 测试结束后关闭线程池,避免资源泄漏
    classUnderTest.executorService.shutdownNow()
}

方案2:优化被测线程的循环逻辑

原被测代码的while(true)忙循环会占用大量CPU,且可能导致测试线程更新时间后,被测线程无法及时读取。给循环添加休眠和中断机制,留出线程调度窗口:

修改被测代码:

public void run() {
    while (!Thread.currentThread().isInterrupted()) {
        if (hasThreeHoursPassed(clock.getTime())) {
            // 核心业务逻辑:时间衰减的热门内容排名计算
            result.set(calculateRanking());
        }
        try {
            // 每次循环休眠100ms,给测试线程留出时间更新时钟
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            break;
        }
    }
}

方案3:用原子类封装时间变量(跨线程可见性保障)

将时间变量换成AtomicLong,确保跨线程的可见性,Mock直接返回原子类的当前值:

def 'test thread with atomic time' () {
    given:
    AtomicLong mockedTime = new AtomicLong(1L)
    MyClock clockMock = Mock(MyClock) {
        getTime() >> { mockedTime.get() }
    }
    ClassUnderTest classUnderTest = new ClassUnderTest(clockMock)

    expect:
    for (int i = 0; i < 100; i++) {
        mockedTime.addAndGet(1000 * 60 * 60 * 3)
        sleep(100)
        classUnderTest.getResult() == calculateExpectedValue(i)
    }

    cleanup:
    classUnderTest.executorService.shutdownNow()
}

关键注意事项

  • 必须添加等待逻辑:被测线程是异步执行的,测试线程不能在更新时间后立刻断言,要给业务逻辑足够的处理时间。
  • 避免资源泄漏:测试结束后务必关闭ExecutorService,防止线程一直后台运行。
  • 业务逻辑可测试性:确保hasThreeHoursPassed和核心排名计算逻辑是纯函数,便于单独验证。

内容的提问来源于stack exchange,提问作者James Wierzba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 03:59:55