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

Android MVI架构单元测试遇NoInteractionsWanted错误求助

这个问题我之前踩过坑,本质是LiveData的默认行为导致的未预期交互,我来给你拆解下解决方案:

问题根源

你看到的NoInteractionsWanted错误,是因为LiveData在观察者订阅时,会自动发送当前持有的初始状态。你的ViewModel里的State LiveData肯定有一个默认的初始值(比如State(isLoading = false, list = emptyList())),当你把mock的testObserver绑定到LiveData上时,它会先收到这个初始状态的onChanged调用,但你的测试代码只验证了Action.Load触发后的loading和loaded两次状态,所以verifyNoMoreInteractions就会检测到这个“漏网”的调用,直接报错。

而且你提到reducer只被调用两次是对的——初始状态是ViewModel初始化时就发送的,并没有经过reducer,所以调试时看不到reducer的调用记录,但LiveData确实已经把初始值发给观察者了。

解决办法

方案1:明确验证初始状态(推荐)

把初始状态的调用也加入验证逻辑,确保所有预期的onChanged都被覆盖:

fun verifyLoadStateSuccess() {
    // 第一步:定义和ViewModel一致的初始状态
    val initialState = State(isLoading = false, list = emptyList())
    val loading = State(isLoading = true)
    val loaded = State(list)

    whenever(listUseCase.loadList()).thenReturn(Observable.just(list))

    // 先验证初始状态的调用
    verify(testObserver).onChanged(initialState)

    viewModel.dispatch(Action.Load)
    testSchedulerRule.triggerActions()

    // 再验证加载流程的状态变化
    inOrder(testObserver) {
        verify(testObserver).onChanged(loading)
        verify(testObserver).onChanged(loaded)
    }

    verifyNoMoreInteractions(testObserver) // 现在就能正常通过了
}

方案2:跳过初始状态验证(临时调试用)

如果你不想纠结初始状态,可以直接移除verifyNoMoreInteractions,或者用Mockito的ignoreStubs()来忽略未 stub 的调用(不过这种方式会降低测试的严谨性):

// 替换原有的verifyNoMoreInteractions
verifyNoMoreInteractions(ignoreStubs(testObserver))

额外提示

如果你的项目用的是AndroidX的TestObserver(而非Mockito的mock Observer),可以用skip(1)来跳过初始值的通知,再验证后续状态:

viewModel.state.test()
    .skip(1) // 跳过初始状态
    .assertValues(loading, loaded)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 06:52:39