首页 > 解决方案 > 使用 Completable.timer 对 RxJava 进行单元测试

问题描述

Android Studio 3.5.2
RxJava2
Kotlin 1.3.50

我有下面的类,它就像一个计时器。

class DelayTimerImp(private val scheduler: IScheduler)
    : DelayTimer {

    override fun createTimeout(delay: Long, timeUnites: TimeUnit): Completable {
        return Completable.timer(delay, TimeUnit.SECONDS, scheduler.main())
    }
}

我正在以这样的方法使用它:

private fun startTimeoutRequest(delay: Long) {
    compositeSubscription.add(delayTimer.createTimeout(delay, TimeUnit.SECONDS)
            .subscribeOn(schedulers.io())
            .subscribe(::timeoutHasExceeded, ::timeoutError))
}

在我的测试类中,它使用TestScheduler来返回io线程。我在嘲笑delayTimer。即使我从未涉足其中thenReturn(Completable.complete())的方法。timeOutHasExceeded我在想如果Completable已经完成了进入那个方法。

@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
}

此外,如果我通过将延迟更改为1

whenever(delayTimer.createTimeout(1, TimeUnit.SECONDS))
                .thenReturn(Completable.complete())

我将crash在以下行中得到一个:

.subscribeOn(schedulers.io())

对此进行单元测试的最佳方法是什么?

提前谢谢了

标签: androidkotlinrx-java2

解决方案


根据本教程,您应该使用 TestScheduler 覆盖这些:

class TestSchedulerProvider(private val scheduler: TestScheduler) : BaseSchedulerProvider {
    override fun computation() = scheduler
    override fun ui() = scheduler
    override fun io() = scheduler
}

然后稍后本教程使用 TestScheduler 如下:

@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)
}

的使用在代码中的advanceTimeBy注释下进行了描述。//HERE:是否可以使用相同的方法,而不是您的:

whenever(delayTimer.createTimeout(1, TimeUnit.SECONDS))
            .thenReturn(Completable.complete())

你会打电话给例如:

testScheduler.advanceTimeBy(1500, TimeUnit.MILLISECONDS)

在您创建超时和我选择的 1500 之后的行中,以便在advanceTimeBy 继续之前真正在计时器中打勾 1 秒。不过,它应该与每个 > 1000 的值都很好,我只是想确定一下;)


推荐阅读