Reproducible with Spring Webflux 5.2.2.RELEASE (via Spring Boot 2.2.2.RELEASE). A sample application is available here: https://github.com/wujek-srujek/reactor-retry-test - please let me know if you would like me to put the complete code here, for now I just copied the relevant parts. Originally posted as a SO question here: https://stackoverflow.com/questions/59406297/test-using-stepverifier-blocks-when-using-spring-webclient-with-retry.

The following client code (Kotlin):

fun send(data: Data): Mono<Void> {
    return webClient
            .post()
            .uri(uri)
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(data)
            .retrieve()
            .toBodilessEntity()
            .doOnSubscribe {
                println(">>> Calling backend, uri: $uri")
            }
//                .retryExponentialBackoff(
//                        maxRetries, firstBackOff, maxBackOff, jitter = true) {
//                    println("<<< Call to $uri failed, will retry (#${it.iteration()} of max 2)")
//                }
            .retryBackoff(maxRetries, firstBackOff, maxBackOff, 0.5)
            .doOnError {
                println("<<< Call to $uri with 2 retries failed with $it")
            }
            .doOnSuccess {
                println("<<< Call to $uri succeeded")
            }
            .then()
}

with the following test:

@Test
fun `test with WebClient`() {
    val responseStatus = HttpStatus.INTERNAL_SERVER_ERROR

    // client.maxRetries = 3
    // server is an instance of okhttp3.mockwebserver.MockWebServer
    repeat(client.maxRetries.toInt()) {
        server.enqueue(MockResponse().setResponseCode(responseStatus.value()))
    }

    StepVerifier.withVirtualTime { client.send(Data("some data")) }
            .expectSubscription()
            .thenAwait(Duration.ofDays(1)) // wait long enough
            .expectNextCount(0)
            .expectError()
            .verify()
}

blocks. A similar test with retry but not using the WebClient works fine. The test blocks at the second retry (i.e. the first retry somehow works fine, the second one blocks indefinitely).

Comment From: rstoyanchev

As explained on StackOverflow this is unrelated to the WebClient and has to do with how virtual time works.

A similar test with retry but not using the WebClient works fine.

As far as that goes, the test does not simulate the error scenario. If you change it to decouple the error signal from the current thread, the same hanging occurs:

@Test
fun `test without WebClient`() {

    val scheduler = Schedulers.newElastic("test")

    StepVerifier
            .withVirtualTime {
                Mono
                        .delay(Duration.ofMillis(100), scheduler)
                        .map{throw RuntimeException()}
                        .retryBackoff(client.maxRetries, client.firstBackOff, client.maxBackOff, 0.5)
                        .then()
            }
            .expectSubscription()
            .thenAwait(Duration.ofDays(1))
            .expectNextCount(0)
            .expectError()
            .verify()

I suppose virtual time does not combine very well with retries, is that accurate @simonbasle?

Comment From: rstoyanchev

I'm closing this ticket since it is unrelated to the Spring Framework.

Comment From: simonbasle

That is accurate, the subsequent retry attempts are scheduled AFTER the virtual clock has been advanced, so the clock is never advanced enough for the sequence to complete.