我正在为使用RxJava 2的类编写单元测试。当在观察器上调用onNext()时,我希望onMenuLoaded()
被调用一次。在代码中,它被成功调用一次。但是当我在单元测试中测试这部分时,该方法被调用了 3 次。
问题是如何使其在测试中仅调用一次,以及为什么在测试中调用的次数比在实际代码中调用的次数多。
//in ViewModel class, under testing
fun loadMenu() {
menuInteractorImpl.getMainMenu()?.subscribeOn(Schedulers.io())?.observeOn(AndroidSchedulers.mainThread())?.subscribe(
{ items ->
onMenuLoaded(items)
},
{ error ->
//error handling logic
}
)?.let { compositeDisposables.add(it) }
}
//Test
@RunWith(PowerMockRunner::class)
@PowerMockRunnerDelegate(MockitoJUnitRunner::class)
@PrepareForTest(MenuInteractorImpl::class, MainMenuViewModel::class)
class MainMenuViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
companion object {
@ClassRule
@JvmField
val schedulers = RxImmediateSchedulerRule()
}
@Before
fun setUp() {
doNothing().`when`(viewModel).startTimerToScrollViewPager()
}
@Test
fun `test load menu calls onMenuLoaded when success`() {
val mockObservable = Observable.just(mockDataFactory.mockMenu).doOnNext {
viewModel.onMenuLoaded(it)
}.subscribeOn(Schedulers.newThread())
Mockito.`when`(menuInteractorImpl.getMainMenu()).thenReturn(mockObservable)
viewModel.loadMenu() //this method is called 3 times
Mockito.verify(viewModel, times(1)).onMenuLoaded(any())
}
从日志中显示了调用该方法的位置
viewModel.loadMenu();
-> at com.example.mainmenu.MainMenuViewModelTest.test load menu calls onMenuLoaded when success(MainMenuViewModelTest.kt:88)
viewModel.loadMenu();
-> at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:182)
viewModel.loadMenu();
-> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
您的真实代码正在onNext
中调用onMenuLoaded(items)
,然后您的模拟可观察量另外从doOnNext
调用它。
您需要删除模拟可观察对象的doOnNext
部分:
@Test
fun `test load menu calls onMenuLoaded when success`() {
val mockObservable = Observable.just(mockDataFactory.mockMenu)
.subscribeOn(Schedulers.newThread())
Mockito.`when`(menuInteractorImpl.getMainMenu()).thenReturn(mockObservable)
viewModel.loadMenu() //this method is called 3 times
Mockito.verify(viewModel, times(1)).onMenuLoaded(any())
}
问题在于不小心将PowerMockito间谍和Mockito模拟结合在一起。在导入Mockito的间谍时,问题得到了解决。
编辑:我再次遇到同样的问题,解决方案是不同的。看起来同样的原因正在结合PowerMockito和Mockito。这次我通过添加一个doNothing
块解决了它
doNothing().`when`(viewModel).myCall(true)