无需注入协程调度器即可实现代码可测试的替代方案
问题根源
你当前测试失败的核心原因是MainCoroutineRule仅替换了Main调度器,而你代码中硬编码的flowOn(Dispatchers.IO)会让channelFlow内部逻辑运行在真实的IO线程池,测试中的advanceTimeBy只能控制测试调度器的虚拟时间,无法干预真实IO线程中的delay逻辑,因此执行断言时Flow还没有发射任何数据,拿到的自然是空数组。
最优解决方案(低侵入注入调度器,官方推荐)
你不需要为了测试在所有业务代码中处处注入调度器,仅需给相关类增加带默认值的构造参数即可,生产环境完全无需修改现有调用逻辑:
- 修改数据提供类,增加调度器默认参数
class NumbersProvider( private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) { val numbersFlow: Flow<Int> = channelFlow { var i = 0 while (i < 10) { delay(100) send(i) i++ } }.flowOn(ioDispatcher) }
- 修改ViewModel,增加调度器默认参数
class NumbersViewModel( private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, private val dataProvider: NumbersProvider = NumbersProvider(ioDispatcher) ): ViewModel() { private val _numbers: MutableLiveData<IntArray> = MutableLiveData(IntArray(0)) val numbers: LiveData<IntArray> = _numbers fun startCollecting() { viewModelScope.launch(Dispatchers.Main) { dataProvider.numbersFlow .onStart { println("start") } .onCompletion { println("end") } .catch { exception -> println(exception.message.orEmpty())} .collect { data -> onDataRead(data) } } } fun onDataRead(data: Int) { _numbers.value = _numbers.value?.plus(data) } }
- 测试时传入测试调度器即可
@Before fun setUp() { viewModel = NumbersViewModel(ioDispatcher = mainCoroutineRule.dispatcher) }
同时建议在测试的advanceTimeBy后调用runCurrent(),确保所有待执行任务都运行完成再执行断言:
@Test fun `provider_provides_10_values`() { viewModel.startCollecting() mainCoroutineRule.advanceTimeBy(2000) mainCoroutineRule.runCurrent() val numbers = viewModel.numbers.value assertThat(numbers?.size).isEqualTo(10) }
该方案仅增加了两个默认参数,对业务代码侵入性极低,也符合协程测试的官方最佳实践,不需要引入全局单例。
零侵入替代方案(仅测试修改)
如果你完全不想修改任何业务代码,可以使用MockK的静态Mock能力直接在测试中替换Dispatchers.IO:
- 引入MockK测试依赖
- 测试类增加静态Mock逻辑
@Before fun setUp() { mockkStatic(Dispatchers::class) every { Dispatchers.IO } returns mainCoroutineRule.dispatcher viewModel = NumbersViewModel() } @After fun tearDown() { unmockkStatic(Dispatchers::class) }
该方案无需修改任何业务代码,缺点是静态Mock会带来少量测试性能开销,多测试类并行执行时需要注意Mock的生命周期管理。
内容的提问来源于stack exchange,提问作者user3921796
相关产品推荐
相关产品推荐

