如何测试Spring WebClient何时重试?

问题描述 投票:0回答:3

我需要实现以下行为:

  • 发出 REST 发布请求
  • 如果返回的响应状态为
    429 Too many requests
    ,请重试最多 3 次,延迟 1 秒
  • 如果第三次重试失败或发生任何其他错误,请记录并向数据库写入内容
  • 如果请求成功(http status 200),记录一些信息

我想使用 Spring WebClient 来实现此目的,并提出了以下代码:

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));

现在我想测试一下重试机制和错误处理是否按我的预期工作。也许我可以使用 StepVerifier 来达到此目的,但我只是不知道如何在我的情况下使用它。有什么有用的提示吗?

rx-java reactive-programming project-reactor spring-webclient
3个回答
8
投票

我认为您可以使用模拟网络服务器来测试它,例如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();
}

如果您将另一个

MockResponse
与状态代码
429
一起排队,验证将失败,与例如相同错误代码
500


3
投票

Dominik Sandjaja 的回答基本上帮助我设置了测试。为了完整起见,我在这里发布了工作版本:

@Test
public void mytest() {
    MockResponse mockResponse = new MockResponse()
        .setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
        .setResponseCode(429).setBody("Too many requests");
    mockWebServer.enqueue(mockResponse);
    mockWebServer.enqueue(mockResponse);
    mockWebServer.enqueue(mockResponse);
    mockWebServer.enqueue(mockResponse);

    Mono<MyClass> responseMono = methodDoingTheAsyncCall.sendAsync(...);
    StepVerifier.create(responseMono)
                .expectError(RetryExhaustedException.class)
                .verify();
}

0
投票

对于测试重试,您还可以使用WireMock Scenarios

@Test
public void test() {
    wireMockServer.stubFor(put(urlEqualTo(URI)).inScenario("Retry")
            .willReturn(aResponse()
                .withStatus(HttpStatus.TOO_MANY_REQUESTS.value())
                .withHeader(HttpHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)))
            .setNewScenarioState("Cause Success");

    wireMockServer.stubFor(put(urlEqualTo(URI)).inScenario("Retry")
        .whenScenarioStateIs("Cause Success")
        .willReturn(aResponse()
            .withStatus(HttpStatus.OK.value())
            .withHeader(HttpHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE)));

    try {
        service.makeWebClientCall();
    } catch (final TooManyRequestsException e) {
        // expected
    }

    wireMockServer.verify(2, putRequestedFor(urlPathMatching(URI)));
}
© www.soinside.com 2019 - 2024. All rights reserved.