RxJava 2 overriding IO scheduler in unit test

前端 未结 4 1988
你的背包
你的背包 2021-02-02 13:28

I\'m trying to test the following RxKotlin/RxJava 2 code:

validate(data)
    .subscribeOn(Schedulers.io())
    .observeO         


        
相关标签:
4条回答
  • 2021-02-02 14:05

    This is the exact syntax that worked for me:

    RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline())
    
    0 讨论(0)
  • 2021-02-02 14:10

    Figured it out! It had to do with the fact that in this code:

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

    validate(data) was returning an Observable, which was emitting the following: emitter.onNext(null). Since RxJava 2 no longer accepts null values, flatMap was not getting called. I changed validate to return a Completable and updated the scheduler override to the following:

    RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
    

    Now the tests pass!

    0 讨论(0)
  • 2021-02-02 14:13

    As an alternative to proposed solutions, this has been working fine for a while in my projects. You can use it in your test classes like this:

    @get:Rule
    val immediateSchedulersRule = ImmediateSchedulersRule()
    

    And the class looks like this:

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

    You can find a way to migrate from TestRule to ExternalResource here and get more info on testing RxJava 2 here.

    0 讨论(0)
  • 2021-02-02 14:25

    I suggest you take a different approach and add a layer of abstraction to your schedulers. This guy has a nice article about it.

    It would look something like this in Kotlin

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

    And then you override that with your own implementation of 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()
        }
    }
    

    And one for testing classes:

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

    Your code would look like this where you call RxJava:

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

    And you'll just override your implementation of SchedulerProvider based on where you test it. Here's a sample project for reference, I am linking the test file that would use the testable-version of SchedulerProvider: https://github.com/Obaied/DingerQuotes/blob/master/app/src/test/java/com/obaied/dingerquotes/QuotePresenterTest.kt#L31

    0 讨论(0)
提交回复
热议问题