如何测试由viewModelScope管控的SharedFlow的数值发射逻辑?
前置依赖
测试需要引入kotlin官方协程测试组件,可选引入Turbine库简化Flow测试逻辑:
dependencies { testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3" // 可选Turbine依赖 testImplementation "app.cash.turbine:turbine:1.0.0" }
问题1:测试初始发射值为Val1
注意事项
原代码中MutableSharedFlow采用默认配置,replay值为0,无历史值缓存能力,ViewModel初始化阶段调用的tryEmit("Val1")会因当时无订阅者直接发射失败。如果业务需要保留最近发射值给后续订阅者,建议调整SharedFlow配置为MutableSharedFlow<String>(replay = 1),以下测试同时覆盖两种场景:
场景1:SharedFlow配置replay=1(推荐业务常用写法)
@OptIn(ExperimentalCoroutinesApi::class) class MyViewModelTest { @Test fun `验证初始发射值为Val1`() = runTest { val viewModel = MyViewModel() // 直接取第一个缓存的replay值即可验证 assertEquals("Val1", viewModel.x.first()) } }
场景2:SharedFlow保持默认replay=0
需要在ViewModel初始化前就启动订阅,否则会错过初始发射值:
@OptIn(ExperimentalCoroutinesApi::class) class MyViewModelTest { @Test fun `验证初始发射值为Val1`() = runTest { val receivedValues = mutableListOf<String>() // 先启动订阅协程 val collectJob = launch { MyViewModel().x.toList(receivedValues) } // 初始化ViewModel触发初始发射 MyViewModel() advanceUntilIdle() assertEquals("Val1", receivedValues.first()) collectJob.cancel() } }
问题2:测试3秒延时后发射Val2
利用协程测试组件的虚拟时间控制能力,无需真实等待3秒即可完成验证:
@OptIn(ExperimentalCoroutinesApi::class) class MyViewModelTest { @Test fun `验证3秒延时后发射Val2`() = runTest { val receivedValues = mutableListOf<String>() val viewModel = MyViewModel() val collectJob = launch { viewModel.x.toList(receivedValues) } // 虚拟时间快进3秒 advanceTimeBy(3000L) advanceUntilIdle() assertTrue("Val2" in receivedValues) collectJob.cancel() } }
搭配Turbine的简化写法:
@OptIn(ExperimentalCoroutinesApi::class) class MyViewModelTest { @Test fun `验证3秒延时后发射Val2`() = runTest { val viewModel = MyViewModel() viewModel.x.test { advanceTimeBy(3000L) assertEquals("Val2", awaitItem()) cancelAndIgnoreRemainingEvents() } } }
额外注意
- 所有测试使用
runTest包裹会自动替换主线程调度器为测试调度器,无需手动处理调度器替换 - 如果使用Junit4,可配合
MainDispatcherRule完成调度器统一替换,避免每个测试单独配置
内容的提问来源于stack exchange,提问作者theapache64
相关产品推荐
相关产品推荐

