测试中基于Flow的LiveData返回旧值的问题如何解决?
问题原因
你的测试失败是典型的协程任务调度差导致的竞态问题:setFirstTime方法内部的viewModelScope.launch任务完成DataStore写入后,Flow会发射新的配置值,但是asLiveData的更新逻辑是投递到主线程调度执行的,你执行断言的时候这个更新任务还没被调度运行,所以拿到的还是LiveData里缓存的旧值。debug时断点带来的延迟刚好等更新完成,所以测试可以通过。
解决方案
第一步:修改测试用例,确保所有协程任务执行完成再断言
将整个测试逻辑放到同一个runBlockingTest块中,调用完setFirstTime后执行advanceUntilIdle(),让测试调度器运行完所有待执行的协程任务,保证LiveData已经收到Flow发射的新值再做断言:
@ExperimentalCoroutinesApi @Test fun `#setFirstTime marks the user as have opened the app at least once`() = mainCoroutineRule.runBlockingTest { assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(true)) mainViewModel.setFirstTime() // 执行所有未完成的协程任务,包括Flow向LiveData同步新值的逻辑 advanceUntilIdle() assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(false)) }
可选优化:为asLiveData指定测试友好的超时配置
asLiveData默认有5000ms的取消超时,测试场景下可以显式把超时设为0,避免不必要的延迟影响测试:
// MainViewModel.kt中修改state的定义 val state = userPreferencesRepository.userPreferencesFlow.asLiveData(timeoutInMs = 0)
验证点
确认你的MainCoroutineRule实现已经正确替换了主线程调度器,参考实现如下:
@ExperimentalCoroutinesApi class MainCoroutineRule( val testDispatcher: TestDispatcher = UnconfinedTestDispatcher() ) : TestWatcher() { override fun starting(description: Description) { super.starting(description) Dispatchers.setMain(testDispatcher) } override fun finished(description: Description) { super.finished(description) Dispatchers.resetMain() } }
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

