Unit testing Rxjava observables that have a delay

蹲街弑〆低调 提交于 2019-12-04 18:14:21

问题


I want to be able to unit test an Observable that has a delayed emission, but without actually waiting for the delay time. Is there a way to do this?

I'm currently using a CountDownHatch to delay the assert, and that works fine, but increases the test run time.

Example:

val myObservable: PublishSubject<Boolean> = PublishSubject.create<Boolean>()
fun myObservable(): Observable<Boolean> = myObservable.delay(3, TimeUnit.SECONDS)

@Test
fun testMyObservable() {
    val testObservable = myObservable().test()
    myObservable.onNext(true)

    // val lock = CountDownLatch(1)
    // lock.await(3100, TimeUnit.MILLISECONDS)
    testObservable.assertValue(true)
}

回答1:


I was able to come up with a complete solution thanks to @aaron-he's answer.

It uses a combination of TestScheduler to advance the time and also RxJavaPlugins to override the default scheduler with the testScheduler. This allowed testing the myObservable() function without modification or needing to pass in a Scheduler.

@Before
fun setUp() = RxJavaPlugins.reset()

@After
fun tearDown() = RxJavaPlugins.reset()

@Test
fun testMyObservable() {
    val testScheduler = TestScheduler()
    RxJavaPlugins.setComputationSchedulerHandler { testScheduler }

    val testObservable = myObservable().test()
    myObservable.onNext(true)

    testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
    testObservable.assertEmpty()
    testScheduler.advanceTimeBy(2, TimeUnit.SECONDS)
    testObservable.assertValue(true)
}



回答2:


TestScheduler is perfect for this. It has a handy method advanceTimeBy(long, TimeUnit) allows you to control the timing. And Observable.delay has an overload method takes a Scheduler.

So just use the default Scheduler.computation() in the myObservable function, and use TestScheduler for unit testing.




回答3:


Found some strange behaviour of PublishSubject it need some time for subscribe, for example the test is failed:

private val scheduler = TestScheduler()
    @Test
    fun publishSubjectFailedTest() {
        val callback: DelayCallback = mock()

        val myPublishSubject: PublishSubject<Boolean> = PublishSubject.create()
        myPublishSubject
            .delay(10, TimeUnit.SECONDS, scheduler)
            .subscribeOn(scheduler)
            .observeOn(scheduler)
            .subscribe(
                Consumer<Boolean> {
                    callback.onCalldown()
                },
                Consumer<Throwable> {

                },
                Action {

                }
            )
        myPublishSubject.onNext(true)
        scheduler.advanceTimeBy(20, TimeUnit.SECONDS)
        verify(callback, times(1)).onCalldown()
    }

But if we add a time of scheduler before call onNext, for example scheduler.advanceTimeBy(1, TimeUnit.NANOSECONDS) then the test will be success:

 @Test
    fun publishSubjectSuccessTest() {
        val callback: DelayCallback = mock()

        val myPublishSubject: PublishSubject<Boolean> = PublishSubject.create()
        myPublishSubject
            .delay(10, TimeUnit.SECONDS, scheduler)
            .subscribeOn(scheduler)
            .observeOn(scheduler)
            .subscribe(
                Consumer<Boolean> {
                    callback.onCalldown()
                },
                Consumer<Throwable> {

                },
                Action {

                }
            )
        scheduler.advanceTimeBy(1, TimeUnit.NANOSECONDS)//added time of scheduler
        myPublishSubject.onNext(true)
        scheduler.advanceTimeBy(20, TimeUnit.SECONDS)
        verify(callback, times(1)).onCalldown()
    }


来源:https://stackoverflow.com/questions/47296993/unit-testing-rxjava-observables-that-have-a-delay

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!