web-dev-qa-db-ja.com

RxJava 2オーバーライドIOスケジューラーの単体テスト

次のRxKotlin/RxJava 2コードをテストしようとしています。

validate(data)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .flatMap { ... }

次のようにスケジューラを上書きしようとしています:

// Runs before each test suite
RxJavaPlugins.setInitIoSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }

ただし、テストを実行すると次のエラーが表示されます。

Java.lang.ExceptionInInitializerError
...
Caused by: Java.lang.NullPointerException: Scheduler Callable result can't be null
    at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.Java:39)
    at io.reactivex.plugins.RxJavaPlugins.applyRequireNonNull(RxJavaPlugins.Java:1317)
    at io.reactivex.plugins.RxJavaPlugins.initIoScheduler(RxJavaPlugins.Java:306)
    at io.reactivex.schedulers.Schedulers.<clinit>(Schedulers.Java:84)

誰かがこの問題を経験しましたか?


RxKotlin/RxJava 1と次のスケジューラーオーバーライドを使用すると、テストは正常に機能しました。

RxAndroidPlugins.getInstance().registerSchedulersHook(object : RxAndroidSchedulersHook() {
    override fun getMainThreadScheduler() = Schedulers.immediate()
})

RxJavaPlugins.getInstance().registerSchedulersHook(object : RxJavaSchedulersHook() {
    override fun getIOScheduler() = Schedulers.immediate()
})
17
Alex B

別のアプローチをとり、抽象化のレイヤーをスケジューラに追加することをお勧めします。この男はそれについて素晴らしい 記事 を持っています。

Kotlinではこのようになります

interface SchedulerProvider {
    fun ui(): Scheduler
    fun computation(): Scheduler
    fun trampoline(): Scheduler
    fun newThread(): Scheduler
    fun io(): Scheduler 
}

そして、SchedulerProviderの独自の実装でそれをオーバーライドします。

class AppSchedulerProvider : SchedulerProvider {
    override fun ui(): Scheduler {
        return AndroidSchedulers.mainThread()
    }

    override fun computation(): Scheduler {
        return Schedulers.computation()
    }

    override fun trampoline(): Scheduler {
        return Schedulers.trampoline()
    }

    override fun newThread(): Scheduler {
        return Schedulers.newThread()
    }

    override fun io(): Scheduler {
        return Schedulers.io()
    }
}

そして、クラスをテストするためのもの:

class TestSchedulerProvider : SchedulerProvider {
    override fun ui(): Scheduler {
        return Schedulers.trampoline()
    }

    override fun computation(): Scheduler {
        return Schedulers.trampoline()
    }

    override fun trampoline(): Scheduler {
        return Schedulers.trampoline()
    }

    override fun newThread(): Scheduler {
        return Schedulers.trampoline()
    }

    override fun io(): Scheduler {
        return Schedulers.trampoline()
    }
}

コードは、RxJavaを呼び出すと次のようになります。

mCompositeDisposable.add(mDataManager.getQuote()
        .subscribeOn(mSchedulerProvider.io())
        .observeOn(mSchedulerProvider.ui())
        .subscribe(Consumer<Quote> {
...

また、テストする場所に基づいて、SchedulerProviderの実装をオーバーライドするだけです。これが参照用のサンプルプロジェクトです。テスト可能なバージョンのSchedulerProviderを使用するテストファイルをリンクしています: https://github.com/Obaied/DingerQuotes/blob/master/app/ src/test/Java/com/obaied/dingerquotes/QuotePresenterTest.kt#L31

24
Solidak

理解した!このコードでは次のことを行う必要があります。

_validate(data)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .flatMap { ... }
_

validate(data)Observableを返していましたが、これはemitter.onNext(null)を発行していました。 RxJava 2はnull値を受け入れなくなったため、flatMapは呼び出されませんでした。 validateを変更してCompletableを返すようにし、スケジューラーのオーバーライドを次のように更新しました。

_RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
_

これでテストに合格しました。

12
Alex B

これは私のために働いた正確な構文です:

RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline())
3
Efi G

提案されたソリューションの代替として、これは私のプロジェクトでしばらくの間うまく機能しています。次のようにテストクラスで使用できます。

@get:Rule
val immediateSchedulersRule = ImmediateSchedulersRule()

そしてクラスは次のようになります:

class ImmediateSchedulersRule : ExternalResource() {

    val immediateScheduler: Scheduler = object : Scheduler() {

        override fun createWorker() = ExecutorScheduler.ExecutorWorker(Executor { it.run() })

        // This prevents errors when scheduling a delay
        override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
            return super.scheduleDirect(run, 0, unit)
        }

    }

    override fun before() {
        RxJavaPlugins.setIoSchedulerHandler { immediateScheduler }
        RxJavaPlugins.setComputationSchedulerHandler { immediateScheduler }
        RxJavaPlugins.setNewThreadSchedulerHandler { immediateScheduler }

        RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediateScheduler }
        RxAndroidPlugins.setMainThreadSchedulerHandler { immediateScheduler }
    }

    override fun after() {
        RxJavaPlugins.reset()
    }

}

TestRuleからExternalResourceに移行する方法を見つけることができます ここ とRxJava 2のテストに関する詳細情報 ここ

2
Sebastian