次の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()
})
別のアプローチをとり、抽象化のレイヤーをスケジューラに追加することをお勧めします。この男はそれについて素晴らしい 記事 を持っています。
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
理解した!このコードでは次のことを行う必要があります。
_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() }
_
これでテストに合格しました。
これは私のために働いた正確な構文です:
RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline())
提案されたソリューションの代替として、これは私のプロジェクトでしばらくの間うまく機能しています。次のようにテストクラスで使用できます。
@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のテストに関する詳細情報 ここ 。