Mono.elapse does not work with StepVerifier?

687 Views Asked by At

According to its Javadoc, Mono.elapse() will produce Mono<Tuple2<Long, T>> where the first value would be the elapsed time between the subscribe and the first next signal.

The following test does not work

StepVerifier.withVirtualTime(() -> Mono.just(1)
                                   .delaySubscription(Duration.ofSeconds(1))
                                   .elapsed(),
            () -> VirtualTimeScheduler.enable(true), 1)
                .thenAwait(Duration.ofSeconds(1))
                .expectNextMatches(tuple2 -> tuple2.getT1() >= 1000 && tuple2.getT2() == 1)
                .verifyComplete();

It will throw exception:

java.lang.AssertionError: expectation "expectNextMatches" failed (predicate failed on value: 11,1)

I was expecting that the time elapsed would be at least 1000ms, but it turns out to be just 11ms.

Do I miss anything here?

1

There are 1 best solutions below

1
On BEST ANSWER

The elapsed() operator indeed uses an underlying TimedScheduler to get clock 'instants' in order to compute the time delta, so when withVirtualTime is used, the underlying scheduler should be a VirtualTimeScheduler...

But there is a bug: such operators, when using a default Scheduler, actually obtain a common instance cached within a wrapper. The problem is that the wrapper doesn't delegate its now method, which is the one used by elapsed.

Since the default implementation for now() is to use System.currentTimeMillis(), what you effectively see is elapsed bypasses the virtual time and report real time deltas.

In order to work around that, until the bug is fixed, you can explicitly create and supply a VirtualTimeScheduler like so:

@Test
public void elapsedWithVirtualTimeWorkaround() {
    //we have to create a reference that will be used in 2 places
    VirtualTimeScheduler vts = VirtualTimeScheduler.create();

    StepVerifier.withVirtualTime(() -> Mono.just(1)
                                           .delaySubscription(Duration.ofSeconds(1))
                                           .elapsed(vts), //this avoids the cache problem
            () -> vts, //the supplied scheduler here will be automatically activated by StepVerifier
            1)
                .thenAwait(Duration.ofSeconds(1)) //this uses the same vts once again
                .expectNextMatches(tuple2 -> tuple2.getT1() >= 1000 && tuple2.getT2() == 1)
                .verifyComplete();
}