使用StepVerifier对Flux.take(持续时间)进行单元测试

时间:2017-06-20 15:42:49

标签: java spring unit-testing project-reactor

我正在使用Spring Reactor Core 3.0.6,我有一个返回Flux的方法:

public Flux<Foo> createFlux(){
    return Flux.<List<Foo>,String>generate(/* generator omitted for clarity's sake */ )
        .take(Duration.ofSeconds(10)
        .flatMap(Flux::fromIterable);
}

生成器函数调用分页的REST api来获取结果,如果API继续返回数据,我希望Flux只运行10秒。

它工作正常,但我想创建一些单元测试,我在创建测试时遇到问题,以验证Flux最多只运行10秒。

我模拟了其余的服务,以便它总是返回数据并写下来:

StepVerifier.withVirtualTime(() -> createFlux())
    .thenAwait(Duration.ofSeconds(10))
    .verifyComplete();

但它失败了:

java.lang.AssertionError: expectation "expectComplete" failed (expected: onComplete(); actual: onNext([my toString() Foo bean]))

我想我应该以某种方式使用生成的项目,但我无法找到正确的StepVerifier方法。

修改

我尝试使用thenConsumeWhile跳过所有项目:

StepVerifier.withVirtualTime(() -> createFlux())
    .thenAwait(Duration.ofSeconds(10))
    .thenConsumeWhile(t -> true)
    .verifyComplete();

但现在测试只是无限期地运行而且永远不会结束。

2 个答案:

答案 0 :(得分:2)

如果你错过了它,reference guide可能会让你走上正确的道路?

除了最常见的expectNext,您必须为序列中的每个项目重复这些内容,如果您知道元素的数量或expectNextCount要跳过元素,则可以使用thenConsumeWhile基于谓词,

答案 1 :(得分:2)

生成器实际上可能非常重要...... StepVerifier受限于无限序列,在使用虚拟时间时更是如此。问题是生成器和thenAwait都在主线程中运行,因此生成器无限制会阻止步进器提前时间,从而阻止序列超时。

由于你想测试take的持续时间,我不认为虚拟时间是对的(你正在测试模拟时间)。我将使用持续时间使createFlux方法可参数化,并在更短的时间内执行StepVerifier.create()

如果你真的想使用某种形式的虚拟时间,我发现让它工作的最低要求是

  1. 通过在测试开始时实例化Scheduler,在非虚拟线程上隔离生成器循环,然后在StepVerifier的subscribeOn(scheduler)中使用Supplier
  2. 首先致电.expectNextCount(1),确保所有订阅都已订阅并且数据开始流动,然后再尝试推进时间。
  3. 像这样:

    public Flux<Integer> createFlux() {
        return Flux.<List<Integer>>generate(sink -> {
            sink.next(Arrays.asList(1, 2, 3));
        })
                .take(Duration.ofSeconds(10))
                .flatMap(Flux::fromIterable);
    }
    
    @Test
    public void so44657525() throws InterruptedException {
        Scheduler scheduler = Schedulers.newSingle("test");
        AtomicInteger adder = new AtomicInteger();
    
        StepVerifier.withVirtualTime(() -> createFlux()
                .subscribeOn(scheduler)
                .doOnNext(v -> adder.incrementAndGet())
        )
                    .expectNextCount(1)
                    .thenAwait(Duration.ofSeconds(10))
                    .thenConsumeWhile(t -> true)
                    .verifyComplete();
    
        System.out.println("Total number of values in generated lists: " + adder.get());
    }
    

    expectNextCount(1)修改为expectNextCount(100_000),我有一个打印Total number of values in generated lists: 102405且耗时40毫秒的游戏。