web-dev-qa-db-ja.com

遅延のあるRxjavaオブザーバブルの単体テスト

遅延が発生するObservableを単体テストできるようにしたいが、実際には遅延時間を待たない。これを行う方法はありますか?

現在CountDownHatchを使用してアサートを遅延させていますが、これは問題なく機能しますが、テストの実行時間が長くなります。

例:

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)
}
13
triad

@ aaron-heの回答のおかげで、完全な解決策を見つけることができました。

TestSchedulerの組み合わせを使用して時間を進め、RxJavaPluginsを使用してデフォルトのスケジューラーをtestSchedulerでオーバーライドします。これにより、変更せずにmyObservable()関数をテストしたり、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)
}
26
triad

TestScheduler はこれに最適です。便利なメソッドadvanceTimeBy(long, TimeUnit)があり、タイミングを制御できます。また、_Observable.delay_には オーバーロードメソッド があり、Schedulerを受け取ります。

したがって、myObservable関数でデフォルトのScheduler.computation()を使用し、単体テストではTestSchedulerを使用します。

7
Aaron He

PublishSubjectの奇妙な動作が見つかりました。サブスクライブにしばらく時間がかかります。たとえば、テストが失敗します:

_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()
    }

_

しかし、schedulerを呼び出す前にonNextの時間を追加した場合、たとえばscheduler.advanceTimeBy(1, TimeUnit.NANOSECONDS)の場合、テストは成功します。

_ @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()
    }
_
1
NickUnuchek