【问题标题】:TestScheduler for generic Observable with RxJava使用 RxJava 实现通用 Observable 的 TestScheduler
【发布时间】:2017-10-10 03:24:00
【问题描述】:

我开始使用TestScheduler。像这样的东西一切正常:

@Test
public void test1() throws Exception {
    //when
    TestScheduler scheduler = new TestScheduler();
    TestObserver<Long> subscriber = new TestObserver<>();

    //run
    Observable
            .interval(1L, TimeUnit.SECONDS, scheduler)
            .subscribeWith(subscriber);

    //check
    scheduler.advanceTimeBy(200, TimeUnit.SECONDS);
    assertEquals(200, subscriber.valueCount());
}

如果我改用这个:

@Test
public void test2() throws Exception {
    //when
    TestScheduler scheduler = new TestScheduler();
    TestObserver<Long> subscriber = new TestObserver<>();

    //run
    Observable
            .interval(1L, TimeUnit.SECONDS)
            .observeOn(scheduler)
            .subscribeOn(scheduler)
            .subscribeWith(subscriber);

    //check
    scheduler.advanceTimeBy(200, TimeUnit.SECONDS);
    assertEquals(200, subscriber.valueCount());
}

测试失败,因为订阅者从未被调用。

我找到的所有示例,使用TestSchedulerObservable.interval 并将调度程序传递给工厂方法,就像我在第一个示例中所做的那样。 我不能使用这种方法的原因是,在真正的应用程序中,observables 并不像这个那么简单,我无法通过调度程序。 我认为像在第二个示例中那样设置Scheduler 会很好,但看起来不是。

将 TestScheduler 用于更通用的 Observable 的正确方法是什么?

不使用 TestScheduler 我可以成功使用这些方法:

@Test
public void test3() throws Exception {
    //when
    Scheduler trampoline = Schedulers.trampoline();

    //run
    TestObserver<Long> test = Observable
            .interval(1L, TimeUnit.SECONDS)
            .observeOn(trampoline)
            .subscribeOn(trampoline)
            .test();

    //check
    test.await(3100,TimeUnit.MILLISECONDS);
    assertEquals(3, test.valueCount());
}

@Test
public void test4() throws Exception {
    //when
    Scheduler trampoline = Schedulers.trampoline();

    //run
    TestObserver<Long> test = Observable
            .fromArray(1L, 2L, 3L)
            .subscribeOn(trampoline)
            .observeOn(trampoline)
            .test();

    //check
    assertEquals(3, test.valueCount());
}

@Test
public void test5() throws Exception {
    //when
    Scheduler trampoline = Schedulers.trampoline();

    //run
    TestObserver<Long> test = Observable
            .fromArray(1L, 2L, 3L)
            .subscribeOn(trampoline)
            .observeOn(AndroidSchedulers.mainThread())
            .test();

    //check
    test.awaitTerminalEvent();
    assertEquals(3, test.valueCount());
}

编辑

如果我使用没有区别

@BeforeClass
public static void setupClass() {
    mScheduler = new TestScheduler();

    RxAndroidPlugins.setInitMainThreadSchedulerHandler(__ -> mScheduler);
    RxJavaPlugins.setIoSchedulerHandler(__ -> mScheduler);
}

@Test
public void test2() throws Exception {
    //when
    TestObserver<Long> subscriber = new TestObserver<>();

    //run
    Observable
            .interval(1L, TimeUnit.SECONDS)
            .observeOn(mScheduler)
            .subscribeOn(mScheduler)
            .subscribeWith(subscriber);

    //check
    mScheduler.advanceTimeBy(200, TimeUnit.SECONDS);
    assertEquals(200, subscriber.valueCount());
}

【问题讨论】:

    标签: unit-testing rx-java


    【解决方案1】:

    如果您想覆盖您可以使用的标准调度程序(用于间隔)。也可以覆盖其他标准调度程序。

    RxJavaPlugins.setComputationSchedulerHandler(scheduler -> testScheduler);
    

    testScheduler 将是您的 TestScheduler。设置好插件后,可以像test1

    那样使用advanceTime

    例子:

    @Test
    // fails because interval schedules on different thread then JUnit-Runner-thread -> fall through
    void notWorkingTest1() throws Exception {
        TestScheduler scheduler = new TestScheduler();
        TestObserver<Long> subscriber = new TestObserver<>();
    
        Observable
                .interval(1L, TimeUnit.SECONDS)
                .observeOn(scheduler)
                .subscribeOn(scheduler)
                .subscribeWith(subscriber);
    
        //check
        scheduler.advanceTimeBy(200, TimeUnit.SECONDS);
        assertEquals(200, subscriber.valueCount());
    }
    
    
    @Test
    // not working because interval will not be scheduled on virtual time -> JUnit-Runner-Thread will close because test observable emits on different thread
    void notWorkingTest2() throws Exception {
        //when
        TestScheduler scheduler = new TestScheduler();
    
        //run
        TestObserver<Long> test = Observable
                .interval(1L, TimeUnit.SECONDS)
                .observeOn(scheduler)
                .subscribeOn(scheduler)
                .test();
    
        scheduler.advanceTimeBy(200, TimeUnit.SECONDS);
    
        test.assertValueCount(200);
    }
    
    @Test
    // runs sync. -> no JUnit-Runner-thread blocking needed
    void workingTest() throws Exception {
        TestScheduler scheduler = new TestScheduler();
        RxJavaPlugins.setComputationSchedulerHandler(s -> scheduler);
    
        TestObserver<Long> test = Observable
                .interval(1L, TimeUnit.SECONDS) // executed on Schedulers.computation()
                .observeOn(scheduler)
                .subscribeOn(scheduler)
                .test();
    
        scheduler.advanceTimeBy(200, TimeUnit.SECONDS);
    
        test.assertValueCount(200);
    }
    

    【讨论】:

    • 这首先行不通,因为我没有使用计算调度程序。无论如何,即使我覆盖了其他调度程序,结果也等同于我在 test2 中的结果。我编辑了我的问题。
    • 请看interval()的文档。您会看到,该间隔使用默认调度程序。默认调度程序是计算。如果未调用订阅者,则您可能使用了另一个调度程序。因此,您需要阻止 JUnit-Runner-Thread,这样该方法不仅会失败并终止 JVM。
    • 我为 test2 添加了一个示例,该示例正在运行并进行了一些解释。
    猜你喜欢
    • 1970-01-01
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    相关资源
    最近更新 更多