reactor-core icon indicating copy to clipboard operation
reactor-core copied to clipboard

expectNoEvent does not fail on emission

Open stefnoten-aca opened this issue 2 years ago • 1 comments

Expected Behavior

expectNoEvent fails when a emission happens

Actual Behavior

expectNoEvent does not fail on an emission

Steps to Reproduce

@Test
void shouldFailButDoesNot() {
    StepVerifier.withVirtualTime(() -> Flux.never().startWith("a"))
                .expectSubscription()
                .expectNoEvent(Duration.ofSeconds(1)) // should fail
                .expectNext("a")
                .verifyTimeout(Duration.ofSeconds(1));
}

Also incorrectly succeeds:

  • Use StepVerifier.create instead of withVirtualTime
  • Use thenCancel().verify() instead of verifyTimeout (apparently a known case since #1440)

Your Environment

  • Reactor version(s) used: reactor-core and reactor-test 3.4.24 (also reproducible on 3.5.2)
  • JVM version (java -version): Temurin-17.0.4+8
  • OS and version (eg uname -a): Darwin Kernel Version 21.6.0

stefnoten-aca avatar Jan 16 '23 10:01 stefnoten-aca

@stefnoten-aca Thanks for finding that!

Unfortunately, no promises of a fix 😔. The issue is related to the StepVerifier design and what you observe could be a design flaw.

The problem is that expectNoEven triggers VirtualTimeScheduler to progress by 1 second. Since the startWith has nothing to do with schedulers, the progress will basically do nothing, hence no events will be produced during the expectNoEvents stage. Unfortunately, we don't have a mechanism to put the expectNoEvents stage back in the queue and wait for another event and check whether the event arrived during expectation or not.

All that said - try to redesign your test and use a different approach to verify event time.

One possible workaround could be the use of elapsed operator in combination with predictable virtual time

@Test
	void shouldFailButDoesNot() {
		StepVerifier.withVirtualTime(() -> Flux.never().startWith("a").elapsed())
		            .expectSubscription()
		            .expectNoEvent(Duration.ofSeconds(1)) // should fail
		            .expectNextMatches(e -> e.getT2().equals("a") && e.getT1() >= 1000)
		            .verifyTimeout(Duration.ofSeconds(1));
	}

OlegDokuka avatar Jan 16 '23 17:01 OlegDokuka