rx-javareactive-programmingproject-reactorspring-webclient

How to test Spring WebClient retry when?


I need to implement following behavior:

I would like to use Spring WebClient for this purpose and came up with this code:

Mono<ClientResponse> response = webClient.post()
            .uri(URI.create("/myuri"))
            .body(BodyInserters.fromObject(request))
            .retrieve()
            .onStatus(httpStatus -> httpStatus.equals(HttpStatus.TOO_MANY_REQUESTS), 
                      response -> Mono.error(new TooManyRequestsException("System is overloaded")))
            .bodyToMono(ClientResponse.class)
            .retryWhen(Retry.anyOf(TooManyRequestsException.class)
                                          .fixedBackoff(Duration.ofSeconds(1)).retryMax(3))
            .doOnError(throwable -> saveToDB(some_id, throwable))
            .subscribe(response -> logResponse(some_id, response));

Now I would like to test if the retry mechanism and error handling works as I expect. May be I could use StepVerifier for this purpose, but I just cannot figure out how to use it in my case. Any useful hints?


Solution

  • I think that you might be able to test this with a mock web server, e.g. MockWebServer.

    @Test
    public void testReactiveWebClient() throws IOException
    {
        MockWebServer mockWebServer = new MockWebServer();
    
        String expectedResponse = "expect that it works";
        mockWebServer.enqueue(new MockResponse().setResponseCode(429));
        mockWebServer.enqueue(new MockResponse().setResponseCode(429));
        mockWebServer.enqueue(new MockResponse().setResponseCode(429));
        mockWebServer.enqueue(new MockResponse().setResponseCode(200)
                                      .setBody(expectedResponse));
    
        mockWebServer.start();
    
        HttpUrl url = mockWebServer.url("/mvuri");
        WebClient webClient = WebClient.create();
    
        Mono<String> responseMono = webClient.post()
                .uri(url.uri())
                .body(BodyInserters.fromObject("myRequest"))
                .retrieve()
                .onStatus(
                        httpStatus -> httpStatus.equals(HttpStatus.TOO_MANY_REQUESTS),
                        response -> Mono.error(new TestStuff.TooManyRequestsException("System is overloaded")))
                .bodyToMono(String.class)
                .retryWhen(Retry.anyOf(TestStuff.TooManyRequestsException.class)
                                   .fixedBackoff(Duration.ofSeconds(1)).retryMax(3));
    
        StepVerifier.create(responseMono)
                .expectNext(expectedResponse)
                .expectComplete().verify();
    
        mockWebServer.shutdown();
    }
    

    If you enqueue another MockResponse with a statuscode 429, the verification will fail, same with e.g. errorcode 500.