RxJava 2在单元测试中覆盖IO调度程序

Ale*_*x B 17 kotlin rx-java rx-kotlin

我正在尝试测试以下RxKotlin/RxJava 2代码:

validate(data)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .flatMap { ... }
Run Code Online (Sandbox Code Playgroud)

我正在尝试覆盖调度程序,如下所示:

// Runs before each test suite
RxJavaPlugins.setInitIoSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
Run Code Online (Sandbox Code Playgroud)

但是,运行测试时出现以下错误:

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)
Run Code Online (Sandbox Code Playgroud)

有没有人遇到过这个问题?


使用RxKotlin/RxJava 1和以下调度程序覆盖时,测试工作正常:

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

RxJavaPlugins.getInstance().registerSchedulersHook(object : RxJavaSchedulersHook() {
    override fun getIOScheduler() = Schedulers.immediate()
})
Run Code Online (Sandbox Code Playgroud)

谢谢!

Sol*_*dak 24

我建议您采用不同的方法,并为您的调度程序添加一个抽象层.这家伙有一篇很好的文章:https://medium.com/@peter.tackage/an-alternative-to-rxandroidplugins-and-rxjavaplugins-scheduler-injection-9831bbc3dfaf

它在Kotlin看起来像这样

interface SchedulerProvider {
    fun ui(): Scheduler
    fun computation(): Scheduler
    fun trampoline(): Scheduler
    fun newThread(): Scheduler
    fun io(): Scheduler 
}
Run Code Online (Sandbox Code Playgroud)

然后用你自己的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()
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一个用于测试课程:

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

你调用RxJava的代码看起来像这样:

mCompositeDisposable.add(mDataManager.getQuote()
        .subscribeOn(mSchedulerProvider.io())
        .observeOn(mSchedulerProvider.ui())
        .subscribe(Consumer<Quote> {
...
Run Code Online (Sandbox Code Playgroud)

您只需SchedulerProvider根据测试位置覆盖您的实现.这是一个示例项目供参考,我链接的测试文件将使用testable:version SchedulerProvider:https://github.com/Obaied/DingerQuotes/blob/master/app/src/test/java/com/obaied/ dingerquotes/QuotePresenterTest.kt#L31

  • 我不是这个,我们需要这一层抽象。感觉就像我们已经在没有这种抽象的情况下进行测试的方式时,只是为了进行测试而正在对生产代码进行大量修改。@Alex提供的答案非常有效。 (2认同)

Ale*_*x B 12

弄清楚了!它与此代码中的事实有关:

validate(data)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .flatMap { ... }
Run Code Online (Sandbox Code Playgroud)

validate(data)回来了Observable,发出以下几点:emitter.onNext(null).由于RxJava 2不再接受null值,flatMap因此未被调用.我更改validate为返回a Completable并将调度程序覆盖更新为以下内容:

RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
Run Code Online (Sandbox Code Playgroud)

现在测试通过了!


Seb*_*ian 6

作为提议的解决方案的替代方案,这在我的项目中已经运行良好一段时间了。您可以在测试类中使用它,如下所示:

@get:Rule
val immediateSchedulersRule = ImmediateSchedulersRule()
Run Code Online (Sandbox Code Playgroud)

这个类看起来像这样:

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

}
Run Code Online (Sandbox Code Playgroud)

您可以在此处找到从 TestRule 迁移到ExternalResource 的方法,并在此处获取有关测试 RxJava 2 的更多信息。


Efi*_*i G 5

这是对我有用的确切语法:

RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline())
Run Code Online (Sandbox Code Playgroud)