Reputation: 2564
I'm trying to test my server call with retrofit and rxJava. I'm using a MVP pattern with koin, and I'm having some problems when I try to test the method that do the call to get the data from the server.
I have a prenter that call the interactor to retrieve the data. Interactor DI is did with koin.
I've done some research here and in google and all the examples that I've been watching don't work for me.
The error that I have is this:
Wanted but not invoked:
callback.onResponseSearchFilm(
[Film(uid=1, id=1724, title=The incredible Hulk, tagline=You'll like him when he's angry., overview=Scientist Bruce Banner scours the planet for an antidote to the unbridled force of rage within..., popularity=22.619048, rating=6.1, ratingCount=4283, runtime=114, releaseDate=2008-06-12, revenue=163712074, budget=150000000, posterPath=/bleR2qj9UluYl7x0Js7VXuLhV3s.jpg, originalLanguage=en, genres=null, cast=null, poster=null, favourite=false), Film(uid=2, id=1724, title=The incredible Hulk, tagline=You'll like him when he's angry., overview=Scientist Bruce Banner scours the planet for an antidote to the unbridled force of rage within..., popularity=22.619048, rating=8.0, ratingCount=4283, runtime=114, releaseDate=2008-06-12, revenue=163712074, budget=150000000, posterPath=/bleR2qj9UluYl7x0Js7VXuLhV3s.jpg, originalLanguage=en, genres=null, cast=null, poster=null, favourite=false), Film(uid=3, id=1724, title=The incredible Hulk, tagline=You'll like him when he's angry., overview=Scientist Bruce Banner scours the planet for an antidote to the unbridled force of rage within..., popularity=22.619048, rating=8.5, ratingCount=4283, runtime=114, releaseDate=2008-06-12, revenue=163712074, budget=150000000, posterPath=/bleR2qj9UluYl7x0Js7VXuLhV3s.jpg, originalLanguage=en, genres=null, cast=null, poster=null, favourite=false)]
);
-> at com.filmfy.SearchImplTest.loadItems_WhenDataIsAvailable(SearchImplTest.kt:30)
Actually, there were zero interactions with this mock.
This is my test
class SearchImplTest: KoinTest {
private val searchImpl: SearchImpl = mock()
private val callback: SearchContract.Callback? = mock()
private val api: RetrofitAdapter = mock()
@Test
fun loadItems_WhenDataIsAvailable() {
`when`(api.getFilms()).thenReturn(Observable.just(filmRequestFacke()))
searchImpl.getfilms(callback)
verify(callback)?.onResponseSearchFilm(fackeFilms())
}
}
My interactor code:
class SearchImpl : AbstractInteractor() {
private val voucherApiServe by lazy {
RetrofitAdapter.create()
}
fun getfilms(callback: SearchContract.Callback?){
disposable = voucherApiServe.getFilms()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> processFilmSearch(result.data, callback)},
{ error -> processError(error) }
)
}
fun processFilmSearch(filmList : ArrayList<Film>?, callback: SearchContract.Callback?){
callback?.onResponseSearchFilm(filmList)
}
.
.
.
My module with koin:
factory<SearchContract.Presenter> { (view: SearchContract.View) -> SearchPresenter(view, mSearchImpl = get()) }
Api call
@GET(Api.ENDPOINT.FILMS)
fun getFilms(): Observable<FilmRequest>
Upvotes: 1
Views: 280
Reputation: 1393
It is because during unit tests system call your method
searchImpl.getfilms(callback)
and before it will finish immediately call
verify(callback)?.onResponseSearchFilm(fackeFilms())
so getfilms() method not invoked and your test fail.
To wait until your rx code will finish you should inject and replace your Schedulers during unit test.
Change code:
fun getfilms(callback: SearchContract.Callback?){
disposable = voucherApiServe.getFilms()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> processFilmSearch(result.data, callback)},
{ error -> processError(error) }
)
}
to:
fun getfilms(callback: SearchContract.Callback?){
disposable = voucherApiServe.getFilms()
.subscribeOn(ioScheduler) //injected scheduler
.observeOn(mainScheduler) //injected scheduler
.subscribe(
{ result -> processFilmSearch(result.data, callback)},
{ error -> processError(error) }
)
}
create Dagger module like:
@Module
class SchedulersModule {
@Provides
@Named(Names.MAIN)
fun main(): Scheduler {
return AndroidSchedulers.mainThread()
}
@Provides
@Named(Names.IO)
fun io(): Scheduler {
return Schedulers.io()
}
@Provides
@Named(Names.COMPUTATION)
fun computation(): Scheduler {
return Schedulers.computation()
}
}
where Names is just a file with string constants (which ofcource as we know have to be different) and in your SearchImpl class inject this schedulers in constructor.
When you will create your SearchImpl class under test use TestScheduler to replace schedulers inside your voucherApiServe.getFilms() chain.
So. The last part is to force rxjava's schedulers to finish work before you will verify result.
your test should look like this:
import io.reactivex.schedulers.TestScheduler
val testScheduler = TestScheduler()
@Before
fun before() {
//you create your SearchImpl class here and use testScheduler to replace real schedulers inside it
}
@Test
fun loadItems_WhenDataIsAvailable() {
`when`(api.getFilms()).thenReturn(Observable.just(filmRequestFacke()))
searchImpl.getfilms(callback)
testScheduler.triggerActions() //Triggers any actions that have not yet been triggered and that are scheduled to be triggered at or before this Scheduler's present time.
verify(callback)?.onResponseSearchFilm(fackeFilms())
}
So this test will work. This also will help you during UI tests (to remove all delays in Observable.timer for example).
Hope it'll help :)
Upvotes: 1