多线程场景下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
相关产品推荐
相关产品推荐

