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

