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

ViewModel+Room单元测试:FakeCacheRepository的Flow不响应更新排查

问题根源

你的FakeCacheRepository实现存在核心问题:getAllChainTransactions()返回的Flow仅会在首次被收集时发射一次当前的txPool列表,后续txPool的更新完全不会触发Flow发射新值。

原因在于普通的flow{} builder是冷流,且你只在其中调用了一次emit,没有任何机制监听txPool的变化并在数据更新时重新发射新值。而ViewModel的uiState依赖这个Flow构建,自然无法感知后续的数据变更。

同时createChainTx()的Flow仅会发射刚添加的单个交易,也无法驱动基于全量交易列表的uiState更新。

修复FakeCacheRepository

用MutableStateFlow维护交易池状态,它能自动在数据变化时向收集者发射新值,完美模拟Room数据库Flow的行为:

private val _txPool = MutableStateFlow<List<ITransaction>>(emptyList())
// 对外暴露不可变的StateFlow
val txPool: StateFlow<List<ITransaction>> = _txPool.asStateFlow()

override fun createChainTx(tx: ITransaction): Flow<ITransaction> {
    return flow {
        // 更新StateFlow的值,自动触发下游收集
        _txPool.value = _txPool.value + tx
        emit(tx)
    }
}

override fun getAllChainTransactions(): Flow<List<ITransaction>> {
    // 直接返回StateFlow,无需额外包装
    return txPool
}
优化测试方案
  1. 监听Flow全量更新:不要只取uiState.value,而是收集整个Flow的更新序列,确保能验证多次数据变更的响应:
@Test
fun `mintToken adds transaction to pendingTx`() = runTest {
    val subj = YourViewModel(fakeRepo)
    val uiStateLogs = mutableListOf<UiState>()
    
    // 启动协程收集uiState的所有更新
    val collectJob = launch {
        subj.uiState.collect { uiStateLogs.add(it) }
    }
    
    // 执行交易操作
    subj.mintToken(to, value, uri)
    advanceUntilIdle()
    
    // 验证最终状态
    assertThat(uiStateLogs.last().pendingTx.size, `is`(1))
    
    // 可追加多次操作验证持续响应
    subj.mintToken(to2, value2, uri2)
    advanceUntilIdle()
    assertThat(uiStateLogs.last().pendingTx.size, `is`(2))
    
    collectJob.cancel()
}
  1. 对齐真实Repository行为:Fake的核心是模拟真实组件的契约,Room的Flow会自动监听数据库变更,你的Fake也要保证数据更新时能主动通知下游,StateFlow是最贴合的模拟方式。
  2. 验证初始化状态:可以在测试开始时先验证uiState的初始值是否符合预期,再执行操作验证更新逻辑。

内容的提问来源于stack exchange,提问作者Joe Dow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 14:50:16