如何模拟 Spring WebFlux WebClient?

新手上路,请多包涵

我们编写了一个小型 Spring Boot REST 应用程序,它在另一个 REST 端点上执行 REST 请求。

 @RequestMapping("/api/v1")
@SpringBootApplication
@RestController
@Slf4j
public class Application
{
    @Autowired
    private WebClient webClient;

    @RequestMapping(value = "/zyx", method = POST)
    @ResponseBody
    XyzApiResponse zyx(@RequestBody XyzApiRequest request, @RequestHeader HttpHeaders headers)
    {
        webClient.post()
            .uri("/api/v1/someapi")
            .accept(MediaType.APPLICATION_JSON)
            .contentType(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromObject(request.getData()))
            .exchange()
            .subscribeOn(Schedulers.elastic())
            .flatMap(response ->
                    response.bodyToMono(XyzServiceResponse.class).map(r ->
                    {
                        if (r != null)
                        {
                            r.setStatus(response.statusCode().value());
                        }

                        if (!response.statusCode().is2xxSuccessful())
                        {
                            throw new ProcessResponseException(
                                    "Bad status response code " + response.statusCode() + "!");
                        }

                        return r;
                    }))
            .subscribe(body ->
            {
                // Do various things
            }, throwable ->
            {
                // This section handles request errors
            });

        return XyzApiResponse.OK;
    }
}

我们是 Spring 的新手,在为这个小代码片段编写单元测试时遇到了麻烦。

是否有一种优雅的(反应式)方式来模拟 webClient 本身或启动 webClient 可以用作端点的模拟服务器?

原文由 Roman 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 636
1 个回答

我们通过提供一个自定义的 ExchangeFunction 来实现这一点,它只是将我们想要的响应返回给 WebClientBuilder


webClient = WebClient.builder()
            .exchangeFunction(clientRequest ->
                    Mono.just(ClientResponse.create(HttpStatus.OK)
                    .header("content-type", "application/json")
                    .body("{ \"key\" : \"value\"}")
                    .build())
            ).build();

myHttpService = new MyHttpService(webClient);

Map<String, String> result = myHttpService.callService().block();

// Do assertions here


如果我们想使用 Mokcito 来验证是否进行了调用或在类中的多个单元测试中重用 WebClient,我们还可以模拟交换函数:

 @Mock
private ExchangeFunction exchangeFunction;

@BeforeEach
void init() {
    WebClient webClient = WebClient.builder()
            .exchangeFunction(exchangeFunction)
            .build();

    myHttpService = new MyHttpService(webClient);
}

@Test
void callService() {
    when(exchangeFunction.exchange(any(ClientRequest.class)))
   .thenReturn(buildMockResponse());
    Map<String, String> result = myHttpService.callService().block();

    verify(exchangeFunction).exchange(any());

    // Do assertions here
}


注意:如果您在调用 when 时遇到与发布者相关的空指针异常,您的 IDE 可能导入了 Mono.when 而不是 Mockito.when

资料来源:

原文由 Renette 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题