You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试由viewModelScope管控的SharedFlow的数值发射逻辑?

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 23:45:01