推进RxJava的TestScheduler进行对delay()的阻塞调用

时间:2018-09-19 13:31:04

标签: java unit-testing junit rx-java rx-java2

使用RxJava2,我有一个桥接反应式和非反应式世界的类。在此类中,有一个覆盖的方法,该方法可能经过长时间处理后会返回一些值。此处理使用Single.create(emitter -> ...) 嵌入到响应世界

处理时间对我来说至关重要,因为在业务逻辑,处理运行时还是处理完成(或取消)之后是否出现第二个订户方面存在差异。 < / p>

现在我要测试此类-因此,我需要较长的处理时间才能仅使用虚拟。 RxJava的TestScheduler抢救了!

要为桥接的基类创建一个模拟延迟的测试实现,我需要在TestScheduler上进行阻塞(!)调用,在该调用中,时间的提前在一个单独的线程上触发。 (否则,将不会触发时间的提前。)

我的问题是,我需要将时间提前至少延迟到“昂贵的计算”受阻为止-否则,时间得更改之前操作员变得活跃,因此,它等待直到永远不会发生的另一次前进。

我可以通过在提前致电之前致电Thread.sleep(100)来解决此问题-但这对我来说似乎有点丑陋……要等多久?对于多个测试,该时间加起来,但是由于计时问题而失败了……。

有人知道如何以一种希望更干净的方式测试这种情况吗? 谢谢!

import io.reactivex.Completable;
import io.reactivex.Single;
import io.reactivex.observers.TestObserver;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.schedulers.TestScheduler;

import java.util.concurrent.TimeUnit;

public class TestSchedulerBlocking {

    public static void main(String[] args) throws Exception {

        TestScheduler testScheduler = new TestScheduler();

        // the task - in reality, this is a task with a overridden method returning some value, where I need to delay the return in a test-implementation.
        Single<String> single = Single.create(emitter -> {

            System.out.println("emitting on " + Thread.currentThread() + " ...");

            // This is the core of my problem: I need some way to test a (synchronous) delay before the next line executes
            // (e.g. method returns, or, in another case, an exception is thrown).
            // Thread.sleep(<delay>) would be straight forward, but I want to use TestScheduler's magic time-advancing in my tests...
            // Using the usual non-blocking methods of RX, everything works fine for testing using the testScheduler, but here it doesn't help.
            // Here I need to synchronize it somehow, that the advancing in time is done *after* this call is blocking.
            // I tried a CountDownLatch in doOnSubscribe, but apparently, that's executed too early for me - I'd need doAfterSubscribe or so...
            // Final word on architecture: I'm dealing with the bridging of the reactive to the non-reactive world, so I can't just change the architecture.
            Completable.complete()
                       .delay(3, TimeUnit.SECONDS, testScheduler)
                       .doOnSubscribe(__ -> System.out.println("onSubcribe: completable"))
                       .blockingAwait();

            System.out.println("<< blocking released >>");

            // this has to be delayed! Might be, for example, a return or an exception in real world.
            emitter.onSuccess("FooBar!");

        });


        System.out.println("running the task ...");
        TestObserver<String> testObserver = single.doOnSubscribe(__ -> System.out.println("onSubcribe: single"))
                                                  .subscribeOn(Schedulers.newThread())
                                                  .test();

        // Without this sleep, the advancing in testScheduler's time is done *before* the doIt() is executed,
        // and therefore the blockingAwait() blocks until infinity...
        System.out.println("---SLEEPING---");
        Thread.sleep(100);

        // virtually advance the blocking delay 
        System.out.println("---advancing time---");
        testScheduler.advanceTimeBy(3, TimeUnit.SECONDS);

        // wait for the task to complete
        System.out.println("await...");
        testObserver.await();

        // assertions on task results, etc.
        System.out.println("finished. now do some testing... values (expected [FooBar!]): " + testObserver.values());

    }

}

[注意:昨天我以更为复杂和冗长的方式提出了这个问题-但由于办公室里太热,而且存在一些缺陷和错误。因此,我已将其删除,现在它更加集中于实际问题。]

1 个答案:

答案 0 :(得分:0)

如果我正确理解了您的问题,则说明您拥有长期运行的服务,并且希望能够测试将受到长期运行服务影响的服务的订阅。我已通过以下方式进行处理。

使用TestScheduler来控制其服务时间,从而按需定义服务。然后使用相同的测试计划程序定义测试刺激。

这模拟了阻止实体。不要使用“阻止”。

Single<String> single = Observable.timer( 3, TimeUnit.SECONDS, scheduler )
                          .doOnNext( _l -> {} )
                          .map( _l -> "FooBar!" )
                          .take( 1 )
                          .toSingle();

// set up asynchronous operations
Observable.timer( 1_500, TimeUnit.MILLISECONDS, scheduler )
  .subscribe( _l -> performTestAction1() );
Observable.timer( 1_900, TimeUnit.MILLISECONDS, scheduler )
  .subscribe( _l -> performTestAction2() );
Observable.timer( 3_100, TimeUnit.MILLISECONDS, scheduler )
  .subscribe( _l -> performTestAction3() );
System.out.println("running the task ...");
TestObserver<String> testObserver = single.doOnSubscribe(__ -> System.out.println("onSubcribe: single"))
                                            .subscribeOn(Schedulers.newThread())
                                            .test();

// virtually advance the blocking delay 
System.out.println("---advancing time---");
testScheduler.advanceTimeBy(4, TimeUnit.SECONDS);

您应该看到发生performTestAction1(),然后发生performTestAction2(),然后single完成,然后出现performTestAction3()。所有这些都是由测试计划程序驱动的。