RxJava-如何通过内部“ toBlocking”调用测试功能

时间:2020-03-31 15:04:26

标签: java observable rx-java reactive-programming

我正在尝试向以下代码段添加一些单元测试:

public List<Stuff> extractStuff(Scheduler scheduler, List<Token> tokens) {
    List<Observable<List<Stuff>>> observables = tokens
            .stream()
            .map(token-> Observable.just(getStuffByToken(token)).subscribeOn(scheduler))
            .collect(Collectors.toList());

    List<Stuff> result = new ArrayList<>();
    for (List<Stuff> stuff: Observable.merge(observables).toBlocking().toIterable()) {
        result.addAll(stuff);
    }

    return result;
}

我希望使用并行性来获取Stuff对象,但是我需要先收集所有对象,然后再进行进一步操作(否则,下一个过程没有任何意义)。

代码按预期工作,但是我在单元测试中苦苦挣扎:

@Test
public void extractStuff() {
    // GIVEN
    TestScheduler scheduler = new TestScheduler();
    List<Token> tokens = buildTokens();
    ...
    // WHEN
    List<Stuff> result = this.instance.extractStuff(scheduler, tokens);
    // Execution never comes to this point...
    // THEN
    ...
}

使用调试器,我可以看到Observable.just(...)看起来不错(我的可观察对象列表不是空的,并且可以在其中看到我的自定义模拟对象)。

问题:执行似乎停留在Observable.merge(observables).toBlocking()表达式中。永远不会调用result.addAll行。

我已经尝试过使用TestScheduler对象做一些事情,但是我无法使其工作。我在Internet上发现的大多数示例都在处理一个返回Observable对象的函数,因此相关的单元测试可以运行scheduler.advanceTimeBy(...);。在我的情况下,由于Observable对象没有直接返回,因此我无法采用这种方法。

非常感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

我不确定所发布的代码是否符合您的期望。您的getStuffByToken(...)方法实际上是在调用线程上而不是Scheduler上调用的。

为简单起见,我将Token替换为Integer,将Stuff替换为String

我的getStuffByToken(...)将返回String的{​​{1}}表示形式,并且还将包括当前Integer的名称:

Thread

我可能使用的是RxJava的不同版本,我没有private List<String> getStuffByToken( Integer token ) { return Arrays.asList( token.toString(), Thread.currentThread().getName() ); } 方法,但有toBlocking(...)-我希望这是等效的:

blockingIterable()

如果我们测试以上内容:

public List<String> extractStuff(Scheduler scheduler, List<Integer> tokens) {
    List<Observable<List<String>>> observables = tokens
            .stream()
            .map(token-> Observable.just(getStuffByToken(token)).subscribeOn(scheduler))
            .collect(Collectors.toList());

    List<String> result = new ArrayList<>();
    for (List<String> stuff: Observable.merge(observables).blockingIterable()) {
        result.addAll(stuff);
    }

    return result;
}

我们回来了:

@Test
public void testExtractStuff()
{
    List<Integer> tokens = Arrays.asList( 1, 2, 3, 4, 5 );
    List<String> result = extractStuff( Schedulers.computation(), tokens );
    System.out.println( result );
}

如您所知,所有[1, main, 3, main, 4, main, 5, main, 2, main] 调用都是在getStuffByToken(...)上执行的。

接下来,未调用测试方法的原因是因为main Thread需要调用TestScheduler来模拟导致处理Rx管道的时间流逝。由于您的方法是阻塞方法,因此使用TestScheduler.advanceTimeBy(...)进行测试并不方便。

考虑到以上两个信息,建议您按照以下步骤进行操作:

TestScheduler

您的生产代码可以调用public Single<List<String>> extractStuff( Scheduler scheduler, List<Integer> tokens ) { return Observable.fromIterable( tokens ) .flatMap( token -> Observable.just( token ) .subscribeOn( scheduler ) .map( this::getStuffByToken ) .flatMap( Observable::fromIterable )) .toList(); } 来解决extractStuff(...).blockingGet()

而且,您可以进行如下测试:

List