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 }
优化测试方案
- 监听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() }
- 对齐真实Repository行为:Fake的核心是模拟真实组件的契约,Room的Flow会自动监听数据库变更,你的Fake也要保证数据更新时能主动通知下游,StateFlow是最贴合的模拟方式。
- 验证初始化状态:可以在测试开始时先验证uiState的初始值是否符合预期,再执行操作验证更新逻辑。
内容的提问来源于stack exchange,提问作者Joe Dow
相关产品推荐
相关产品推荐

