I'm trying to unit test my viewmodel code, which does a server polling to return data as soon as its Status == ELIGIBLE
My problem is, it always assert when it's still loading (repeating), and not waiting for the onSuccess to be called to assert the correct status.
I've put some logs to track what's happening:
doOnSubscribe called
repeatWhen called
doOnNext called
takeUntil called
doOnNext called
takeUntil called
As you can see, repeatWhen and takeUntil are called twice (which is expected), but after that, no onSuccess called.
And eventually the test fails with this message Caused by: java.lang.AssertionError: expected:<SUCCESS> but was:<LOADING>
If I removed the failing line, the next assertion would fail too with message:
java.lang.AssertionError:
Expected :ELIGIBLE
Actual :null
Which mean the onSuccess method is not yet reached, and is still loading.
I also don't prefer using Schedulers.trampoline() .. it works, but it waits for 5 secs synchronously. I prefer to use TestScheduler.advanceByTime() instead.
Here's the client code:
fun startStatusPolling() {
val pollingSingle = shiftPayService.obtainCardStatus()
.repeatWhen {
println("repeatWhen called")
//POLLING_INTERVAL_SECONDS = 5
it.delay(POLLING_INTERVAL_SECONDS, TimeUnit.SECONDS)
}
.takeUntil { item ->
println("takeUntil called")
item.cardStatus != Status.PENDING
}.doOnNext {
println("doOnNext called")
}
.lastElement()
.toSingle()
subscribe(pollingSingle, pollingStatusLiveData)
}
And my test class:
@RunWith(HomebaseRobolectricTestRunner::class)
@LooperMode(LooperMode.Mode.PAUSED)
class CardViewModelTest {
lateinit var viewModel: CardViewModel
var testScheduler = TestScheduler()
@Before
fun setup() {
RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { testScheduler }
val cardStatusPending: CardStatus = mockk(relaxed = true) {
every { status } returns Status.PENDING
}
val cardStatusEligible: CardStatus = mockk(relaxed = true) {
every { status } returns Status.ELIGIBLE
}
val cardService: CardService = spyk {
every { obtainCardStatus() } returnsMany listOf(
Single.just(cardStatusPending),
Single.just(cardStatusEligible)
)
}
viewModel = CardViewModel(cardService)
}
@Test
fun testCardStatusPolling() {
viewModel.startStatusPolling()
shadowOf(Looper.getMainLooper()).idle()
testScheduler.advanceTimeBy(5, TimeUnit.SECONDS)
//after 5 sec delay, single is resubscibed, returning the second single cardStatusEligible
assertEquals(Result.Status.SUCCESS, viewModel.pollingStatusLiveData.value?.status)
assertEquals(EligiblityStatus.ELIGIBLE, viewModel.pollingStatusLiveData.value?.data?.eligibilityStatus)
}
}
from Unit testing RxJava repeatWhen operator always stuck on the first iteration
No comments:
Post a Comment