Osheep

时光不回头,当下最重要。

RxJava 之 TestScheduler

TestScheduler 是专门用于测试的调度器,跟其他调度器的区别是TestScheduler只有被调用了时间才会继续。TestScheduler是一种特殊的、非线程安全的调度器,用于测试一些不引入真实并发性、允许手动推进虚拟时间的调度器。

在 RxJava2.x 中,原先RxJava1.x的Schedulers.test()被去掉了,想要获得TestScheduler对象可以通过直接new TestScheduler()的方式来实现。

TestScheduler 所包含的方法并不多,下面罗列几个关键的方法。

advanceTimeTo

将调度器的时钟移动到某个特定时刻。

例如:

时钟移动到10毫秒。

scheduler.advanceTimeTo(10, TimeUnit.MILLISECONDS);

时钟移动到20毫秒。

scheduler.advanceTimeBy(20, TimeUnit.MILLISECONDS);

下面的列子展示了0秒、20秒、40秒会打印不同的结果。

        TestScheduler scheduler = new TestScheduler();

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("immediate");
            }
        });

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("20s");
            }
        },20, TimeUnit.SECONDS);

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("40s");
            }
        },40, TimeUnit.SECONDS);

        scheduler.advanceTimeTo(1, TimeUnit.MILLISECONDS);
        System.out.println("virtual time: " + scheduler.now(TimeUnit.MILLISECONDS));

        scheduler.advanceTimeTo(20, TimeUnit.SECONDS);
        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeTo(40, TimeUnit.SECONDS);
        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

执行结果:

immediate
virtual time: 1
20s
virtual time: 20
40s
virtual time: 40

我们可以看到使用advanceTimeTo之后,移动不同的时间点会打印不同的内容。

当然,advanceTimeTo()也可以传负数,表示回到过去的时间点。但是一般不推荐这种用法。

advanceTimeBy

将调度程序的时钟按指定的时间向前移动。

例如:

时钟移动了10毫秒。

scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);

再次调用刚才的方法,时钟又会移动了10毫秒。此时,时钟移动也到20毫秒,这是一个累加的过程。

scheduler.advanceTimeBy(10, TimeUnit.MILLISECONDS);

下面的例子使用了timer操作符,timer是按照指定时间延迟发送的操作符,timer()并不会按周期地执行。该例子展示了2秒后atomicLong会自动加1

        TestScheduler scheduler = new TestScheduler();

        final AtomicLong atomicLong = new AtomicLong();
        Observable.timer(2, TimeUnit.SECONDS, scheduler).subscribe(new Consumer<Long>() {
            @Override
            public void accept(final Long value) throws Exception {
                atomicLong.incrementAndGet();
            }
        });

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

执行结果:

atomicLong's value=0, virtual time:0
atomicLong's value=0, virtual time:1
atomicLong's value=1, virtual time:2

这个结果符合预期,最初atomicLong为0,时钟移动到1秒它的值仍然为0,时钟再移动1秒相当于时钟移动到2秒,所以它的值变为1。

当然,advanceTimeBy()也可以传负数,表示回到过去。

        TestScheduler scheduler = new TestScheduler();

        final AtomicLong atomicLong = new AtomicLong();
        Observable.timer(2, TimeUnit.SECONDS, scheduler).subscribe(new Consumer<Long>() {
            @Override
            public void accept(final Long value) throws Exception {
                atomicLong.incrementAndGet();
            }
        });

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(1, TimeUnit.SECONDS);

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(-1, TimeUnit.SECONDS);

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(2, TimeUnit.SECONDS);

        System.out.println("atomicLong's value="+atomicLong.get() + ", virtual time:" + scheduler.now(TimeUnit.SECONDS));

执行结果:

atomicLong's value=0, virtual time:0
atomicLong's value=0, virtual time:1
atomicLong's value=0, virtual time:0
atomicLong's value=1, virtual time:2

triggerActions

triggerActions不会修改时间,它执行任何计划中的但是未启动的任务,已经执行过的任务不会再启动。

        TestScheduler scheduler = new TestScheduler();

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("immediate");
            }
        });

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("20s");
            }
        },20, TimeUnit.SECONDS);

        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

执行结果:

virtual time: 0

稍微改一下代码,增加scheduler.triggerActions()

        TestScheduler scheduler = new TestScheduler();

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("immediate");
            }
        });

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("20s");
            }
        },20, TimeUnit.SECONDS);

        scheduler.triggerActions();
        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

执行结果:

immediate
virtual time: 0

此时由于执行了triggerActions(),所以打印了immediate。

再改一下,增加advanceTimeBy()

        TestScheduler scheduler = new TestScheduler();

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("immediate");
            }
        });

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("20s");
            }
        },20, TimeUnit.SECONDS);

        scheduler.triggerActions();
        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(20,TimeUnit.SECONDS);

执行结果:

immediate
virtual time: 0
20s

如果将triggerActions()放在最后,看看效果

        TestScheduler scheduler = new TestScheduler();

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("immediate");
            }
        });

        scheduler.createWorker().schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("20s");
            }
        },20, TimeUnit.SECONDS);


        System.out.println("virtual time: " + scheduler.now(TimeUnit.SECONDS));

        scheduler.advanceTimeBy(20,TimeUnit.SECONDS);
        scheduler.triggerActions();

执行结果:

virtual time: 0
immediate
20s

因为已经使用了advanceTimeBy(),所以即使再调用triggerActions()也不会执行已经启动过的任务。

总结

对于测试一些需要精确时间的任务,TestScheduler还是很有用处的,可以节省很多等待的时间。

参考资料:

  1. http://blog.chengyunfeng.com/?p=979
  2. https://github.com/ReactiveX/RxJava/wiki/What’s-different-in-2.0
点赞