Android Studio 3.5.2
RxJava2
Kotlin 1.3.50
I have the following class that acts like a timer.
class DelayTimerImp(private val scheduler: IScheduler)
: DelayTimer {
override fun createTimeout(delay: Long, timeUnites: TimeUnit): Completable {
return Completable.timer(delay, TimeUnit.SECONDS, scheduler.main())
}
}
And I am using it in a method like this:
private fun startTimeoutRequest(delay: Long) {
compositeSubscription.add(delayTimer.createTimeout(delay, TimeUnit.SECONDS)
.subscribeOn(schedulers.io())
.subscribe(::timeoutHasExceeded, ::timeoutError))
}
And in my test class and it uses the TestScheduler
to return the io
thread. I am mocking the delayTimer. Even though I thenReturn(Completable.complete())
the methods in the timeOutHasExceeded
never go into them. I am thinking that if Completable
has completed in go into that method.
@Test
fun `should reload and show loading state`() {
// Arrange
whenever(delayTimer.createTimeout(10, TimeUnit.SECONDS))
.thenReturn(Completable.complete())
// Act
vipInformationPresenterImp.tryAgainTapped()
// Assert methods in the timeOutHasExceeded
}
Also If I change the following to this by changing the delay to 1
:
whenever(delayTimer.createTimeout(1, TimeUnit.SECONDS))
.thenReturn(Completable.complete())
I will get a crash
on the following line:
.subscribeOn(schedulers.io())
What is the best way to unit test this?
Many thanks in advance
According to this tutorial you should override these with TestScheduler:
class TestSchedulerProvider(private val scheduler: TestScheduler) : BaseSchedulerProvider {
override fun computation() = scheduler
override fun ui() = scheduler
override fun io() = scheduler
}
Then later the tutorial makes use of TestScheduler as follows:
@Test
fun delayTestExample() {
//given
val presenter = DemoPresenter(testSchedulerProvider, view, service)
given(service.getSomeRemoteData()).willReturn(Single.just(5))
val delayInMillis = 1000L
//when
presenter.getSomeDataWithDelay(delayInMillis)
//then
then(view).should(never()).showData(anyInt())
// HERE:
testScheduler.advanceTimeBy(delayInMillis, TimeUnit.MILLISECONDS)
then(view).should().showData(5)
}
The use of advanceTimeBy
is described under comment //HERE:
in the code. Could the same approach be used so that instead of your:
whenever(delayTimer.createTimeout(1, TimeUnit.SECONDS))
.thenReturn(Completable.complete())
you would call just for example:
testScheduler.advanceTimeBy(1500, TimeUnit.MILLISECONDS)
in the line after you create the timeout and that 1500 I selected so that the 1 second really gets ticked in the timer, before the advanceTimeBy continues. It should go well with every value > 1000 though, I just want to be sure enough ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With