Background:
I have a simple application that fetches movie list using rests API call. The project structure is given below,
Activity -> ViewModel -> Repository -> ApiService (Retrofit Interface)
-
The activity subscribes to a LiveData and listens for events changes
-
The ViewModel hosts the MediatorLiveData observed by the activity. Initially the ViewModel sets a
Resource.loading(..)value in MediatorLiveData. -
The ViewModel then calls the repository to get the movie list from ApiService
-
The ApiService returns a LiveData of either
Resource.success(..)orResource.error(..) -
The ViewModel then merges LiveData result from ApiService in MediatorLiveData
My Queries:
- Inside the unit test, only the first emit
Resource.loading(..)is made by MediatorLiveData from ViewModel. The MediatorLiveData never emits any data from the repository. - Is this the right way to handle loading, success and error events with a single MediatorLiveData?
ViewModel.class
private var discoverMovieLiveData: MediatorLiveData<Resource<DiscoverMovieResponse>> = MediatorLiveData()
fun observeDiscoverMovie(): LiveData<Resource<DiscoverMovieResponse>> {
return discoverMovieLiveData
}
fun fetchDiscoverMovies(page: Int) {
discoverMovieLiveData.value = Resource.loading(null) // this emit get observed immediately
val source = movieRepository.fetchDiscoverMovies(page)
discoverMovieLiveData.addSource(source) {
discoverMovieLiveData.value = it // never gets called
discoverMovieLiveData.removeSource(source)
}
}
Repository.class
fun fetchDiscoverMovies(page: Int): LiveData<Resource<DiscoverMovieResponse>> {
return LiveDataReactiveStreams.fromPublisher(
apiService.fetchDiscoverMovies(page)
.subscribeOn(Schedulers.io())
.map { d ->
Resource.success(d) // never gets called in unit test
}
.onErrorReturn { e ->
Resource.error(ApiErrorHandler.getErrorByThrowable(e), null) // // never gets called in unit test
}
)
}
Unit Test
@Test
fun loadMovieListFromNetwork() {
val mockResponse = DiscoverMovieResponse(1, emptyList(), 100, 10)
val call: Flowable<DiscoverMovieResponse> = successCall(mockResponse) // wraps the retrofit result inside a Flowable<DiscoverMovieResponse>
whenever(apiService.fetchDiscoverMovies(1)).thenReturn(call)
viewModel.fetchDiscoverMovies(1)
verify(apiService).fetchDiscoverMovies(1)
verifyNoMoreInteractions(apiService)
val liveData = viewModel.observeDiscoverMovie()
val observer: Observer<Resource<DiscoverMovieResponse>> = mock()
liveData.observeForever(observer)
verify(observer).onChanged(
Resource.loading(null) // never get other events e.g. Resource.success(..)
)
}
Resource is a generic wrapper class that wraps data for different scenario e.g. loading, success, error.
class Resource<out T>(val status: Status, val data: T?, val message: String?) {
.......
}
from jUnit test with LiveData doesn't execute map operator in RxJava
No comments:
Post a Comment