Kotlin协程runTest含delay调用测试失败,疑问求解
为什么UnconfinedTestDispatcher下runTest不会自动跳过delay?
核心原因在于UnconfinedTestDispatcher的设计特性,以及runTest默认的等待逻辑:
1. UnconfinedTestDispatcher的执行逻辑
UnconfinedTestDispatcher的“无限制”并不是指会自动跳过所有挂起操作,它的行为是:
- 立即执行当前协程的同步代码块;
- 一旦遇到挂起点(比如
delay),会暂停当前协程并退出执行流,不会自动模拟时间流逝来完成挂起。
所以当你在launch里调用delay时,这个子协程会卡在挂起点,不会继续执行后续更新result的代码。
2. runTest的默认等待范围
runTest默认只会等待自己块内的主协程完成,不会主动追踪并等待所有子协程的执行。当主协程跑完断言代码时,子协程还因为delay挂着,自然拿不到更新后的result,导致断言失败。
3. 为什么移除delay或加advanceUntilIdle()能解决问题
- 移除delay:子协程里没有了挂起点,UnconfinedTestDispatcher会一口气执行完整个子协程,
result被正确更新,断言自然通过。 - 调用advanceUntilIdle():这个方法会告诉测试调度器,主动推进时间直到所有挂起的协程都完成。此时
delay(1000)的时间会被模拟走完,子协程继续执行到结束,result更新后断言通过。
举个直观的代码对比:
失败的测试代码
@Test fun testWithDelay() = runTest(UnconfinedTestDispatcher()) { var result = 0 launch { delay(1000) result = 1 } assertEquals(1, result) // 断言失败,result仍为0 }
修复后的测试代码
@Test fun testWithAdvance() = runTest(UnconfinedTestDispatcher()) { var result = 0 launch { delay(1000) result = 1 } advanceUntilIdle() // 推进时间至所有协程完成 assertEquals(1, result) // 断言成功 }
内容的提问来源于stack exchange,提问作者Goltsev Eugene
相关产品推荐
相关产品推荐

