简体   繁体   中英

Unit testing RxJava + Retrofit call inside the method

The method I want to test contains of two calls to the retrofit service:

 internal fun poll(): Completable {
    return presenceService.askForFrequency(true).toObservable()
            .flatMap { it -> Observable.interval(it.frequency, TimeUnit.SECONDS, Schedulers.io()) }
            .flatMapCompletable { _ -> presenceService.sendHeartbeat() }
            .subscribeOn(Schedulers.io())
            .retry()
}

The presenceService is injected in the class, so I provide the mocked one for the test:

val frequency = PublishSubject.create<Presence>()
val heartbeat = PublishSubject.create<Unit>()
val mockPresenceService = mock<PresenceService> {
    on { askForFrequency(any()) } doReturn frequency
    on { sendHeartbeat() } doReturn heartbeat
}

The test, that checks that askForFrequency method is called works correctly, but test that checks that the polling request is sent never works:

@Test
fun presenceService_sentHeartbeat() {
    RxJavaPlugins.setIoSchedulerHandler { scheduler }

    frequency.onNext(Presence(1)) //polls with 1s interval
    heartbeat.onNext(Unit)
    presenceMaintainer.onActivityResumed(any())
    scheduler.advanceTimeBy(2, TimeUnit.SECONDS)
    verify(mockPresenceService).askForFrequency(true) //works correctly
    verify(mockPresenceService).sendHeartbeat() //never works
}

The logs from the unit test run are:

Wanted but not invoked:
presenceService.sendHeartbeat();

However, there was exactly 1 interaction with this mock:
presenceService.askForFrequency(true);

The question is: how to test that the second method ( sendHeartbeat ) is also called (possibly several times) ?

Meanwhile I found out that the problem lies in the second flatmap, because the test for this method works correctly (verifies that method was called 60 times):

 internal fun pollTest(): Observable<Presence> {
    return Observable.interval(1, TimeUnit.SECONDS, Schedulers.io())
            .subscribeOn(Schedulers.io())
            .flatMap { it -> presenceService.askForFrequency(true).toObservable() }
}

@Test
fun presenceService_sentHeartbeat() {
    frequency.onNext(Presence(1))
    val result = arrayListOf<Unit>()
    presenceMaintainer.pollTest().subscribe({ t -> result.add(Unit) })
    Thread.sleep(60*1000)
    println(result.size)
    verify(mockPresenceService, Times(60)).askForFrequency(true)
}

But when I change the order of the calls to askForFrequency -> map to interval -> map each tick to poll call , test stops working and mock is called only once.

By default, Observable.interval() runs on the computation scheduler, and not the io scheduler. That means, that the 2 second wait will be run in real time, so your test will finish 2 seconds before the call to sendHeartBeat() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM