如何通过Kotlin协程在单线程运行测试并解决测试失败问题
解决方案
问题根源
- 业务类
MainClass硬编码了Dispatchers.IO和Dispatchers.Main,测试时runTest提供的单线程测试调度器无法替换这些硬编码的调度器,导致协程在真实线程池中异步执行,测试函数会直接走到断言步骤,此时value还没被修改。 - 要实现单线程执行所有协程,需要让业务类的调度器可配置,测试时统一使用
runTest的单线程TestDispatcher。
修改步骤
1. 重构业务类,支持调度器注入
把硬编码的调度器改为可传入的参数,默认使用真实调度器,测试时替换为测试调度器:
class MainClass( private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate ) { val scope = CoroutineScope(SupervisorJob() + mainDispatcher) var value = 0 fun run() { scope.launch(ioDispatcher) { delay(1000) withContext(mainDispatcher) { withContext(ioDispatcher) { delay(1000) withContext(mainDispatcher) { value = 1 } } } } } }
2. 修改测试类,注入单线程测试调度器
在runTest中获取测试调度器,注入到MainClass,这样所有协程都会在这个单线程调度器中执行,runTest会自动等待所有协程完成后再执行断言:
class TestTest { @Test fun test() = runTest { val testDispatcher = this.testDispatcher val test = MainClass( ioDispatcher = testDispatcher, mainDispatcher = testDispatcher ) test.run() assertTrue(test.value == 1) } }
为什么这样能解决问题
- 调度器注入后,测试时所有协程都使用
runTest提供的单线程TestDispatcher,满足"所有协程在单线程执行"的要求。 runTest会自动跟踪所有在测试调度器上启动的协程,等待它们全部完成后才会结束测试函数,这样断言时value已经被正确修改为1。
内容的提问来源于stack exchange,提问作者Nick Nick
相关产品推荐
相关产品推荐

