Issue
The following is the test code for my ViewModel
class. My app architecture is based on MVI. Basically, I’m trying to mock my repo and state classes. In the test function I wrote below, I’m trying to check if state is changed in correct order when I successfully load a news list from an API.
class NewsListViewModelTest {
//...
@get:Rule
val testSchedulerRule RxTestSchedulerRule()
private lateinit var testSubject: NewsListViewModel
private val loadingState NewsListState(state State.LOADING)
private val newsRepo mock<NewsRepository>()
private val observer mock<Observer<NewsListState>>()
@Before
fun setUp() {
testSubject NewsListViewModel(newsRepo)
testSubject.observableState.observeForever(observer)
}
@Test
fun `Given news list successfully loaded, when action LoadNewsList is received, then state contains news list`() {
// GIVEN
val newsList listOf(News("title", "description", Date(), "image"))
val successState NewsListState(newsList newsList, state State.DATA)
whenever(newsRepo.loadAll("keyword", 1, 1))
.thenReturn(Observable.just(newsList))
// WHEN
testSubject.dispatch(NewsListAction.LoadNewsList("keyword"))
testSchedulerRule.triggerActions()
// THEN
inOrder(observer) {
verify(observer).onChanged(loadingState)
verify(observer).onChanged(successState)
}
verifyNoMoreInteractions(observer)
}
}
However, when I run this test, I’m getting the following error in the first line inside inOrder(observer){ ... }
:
Wanted but not invoked:
observer.onChanged(
NewsListState(newsList[], stateLOADING, errorMessage)
);
-> at [packagename].NewsListViewModelTest.Given news list failed to load, when action LoadNewsList is received, then state contains error(NewsListViewModelTest.kt:77)
Actually, there were zero interactions with this mock.
And these are my testing dependencies:
testImplementation 'junit:junit:4.12'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
What might be the reason of this error?
Edit — My RxTestSchedulerRule
class:
class RxTestSchedulerRule(private val testScheduler: TestScheduler TestScheduler()) : Scheduler(),
TestRule {
override fun apply(base: Statement, description: Description?): Statement {
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
RxJavaPlugins.setNewThreadSchedulerHandler { testScheduler }
RxJavaPlugins.setSingleSchedulerHandler { testScheduler }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
return base
}
override fun createWorker() testScheduler.createWorker()
fun triggerActions() testScheduler.triggerActions()
}
Edit 2 — My dispatch function:
fun dispatch(?) {
newsRepository.loadAll(keyword it.keyword,
pageSize pageSize,
page page)
.subscribeOn(Schedulers.io())
.map<NewsListChange> { newsList ->
NewsListChange.Data(newsList) }
.defaultIfEmpty(NewsListChange.Data(emptyList()))
.onErrorReturn { throwable ->
NewsListChange.Error(throwable) }
.startWith(NewsListChange.Loading)
}
Solution
please try instead of this line
whenever(newsRepo.loadAll("keyword", 1, 1))
.thenReturn(Observable.just(newsList))
put
whenever(newsRepo.loadAll(anyString(), anyInt(), anyInt()))
.thenReturn(Observable.just(newsList))
Answered By – Lena Bru