繁体   English   中英

Spring Webflux 如何为 WebClient Junit 模拟响应为 Mono.error

[英]Spring Webflux how to Mock response as Mono.error for WebClient Junit

我正在研究 Spring WebFlux 项目,

我正在调用第三方 API 来使用 WebClient 创建实体。

如果 WebClient 收到 4xx 作为响应代码,我想保留错误。

以下是请求第三方 API 的代码

API.java

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

class API {
  public Mono<Response> create(Request req) {
    return WebClient.builder()
        .baseUrl("http://localhost:8080")
        .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
        .build()
        .post()
        .uri("/v1/student")
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .bodyValue(req.getData())
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, response -> handleError(response))
        .bodyToMono(Response.class);
  }

  private Mono<? extends ClientException> handleError(ClientResponse response) {
    return response
        .bodyToMono(ErrorResponse.class)
        .map(errorResponse -> new ClientException(errorResponse));
  }
}

如果服务器返回 4xx 响应,那么我只是将其转换为特定的错误响应。

调用方法如下

APIService.java

public class APIService {
  API api;
  private ResponseHandler responseHandler;
  ErrorProcessor errorProcessor;


  public APIService(API api, ResponseHandler responseHandler, ErrorProcessor errorProcessor) {
    this.api = api;
    this.responseHandler = responseHandler;
    this.errorProcessor = errorProcessor;

  }

  public void process(Request request) {
    api.create(request)
        .doOnSuccess(response -> responseHandler.process(response))
        .doOnError(
            throwable -> {
              ClientException ce = (ClientException) throwable;
              errorProcessor.process(ce);
            })
        .block();
  }
}

请求.java

public class Request {
    final Map<String, Integer> data;

  public Request(int id) {
    data = new HashMap<>();
    data.put("counter", id);
  }

    public Map<String, Integer> getData() {
    return data;
  }
}

响应.java

public class Response {
  int id;
  public Response(int id) {
    this.id = id;
  }
}

响应处理程序

import java.util.logging.Logger;

public class ResponseHandler {

  Logger log = Logger.getLogger(ResponseHandler.class.getName());

  public void process(Response response) {

    log.info("Id=" + response.id);
  }
}

异常处理类

客户端异常.java

public class ClientException extends RuntimeException {

  private ErrorResponse errorResponse;

  public ClientException(ErrorResponse errorResponse) {
    this.errorResponse = errorResponse;
  }

  public ErrorResponse getErrorResponse() {
    return errorResponse;
  }
}

错误处理器

错误处理器

import java.util.logging.Logger;

public class ErrorProcessor {
  Logger log = Logger.getLogger(ErrorProcessor.class.getName());

  public void process(ClientException ce) {

    log.info(ce.getErrorResponse().getErrorCode() + "=" + ce.getErrorResponse().getMessage());
  }
}

API调用返回的错误响应

错误响应.java

public class ErrorResponse {

  String errorCode;
  String message;

  public ErrorResponse(String errorCode, String message) {
    this.errorCode = errorCode;
    this.message = message;
  }

  public String getErrorCode() {
    return errorCode;
  }

  public String getMessage() {
    return message;
  }
}

在编写 Junit 时如下

APITest.java

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Mono;

@ExtendWith(MockitoExtension.class)
public class APITest {
  @Mock API api;
  @Mock ErrorProcessor errorProcessor;
  @Mock ResponseHandler responseHandler;
  @InjectMocks APIService apiService;

  @Test
  public void givenRequest_shouldCreate() {
    // given
    Request request = new Request(1);
    Mockito.when(api.create(request)).thenReturn(Mono.just(new Response(1)));
    // when
    apiService.process(request);
    // then
    Mockito.verify(api, Mockito.times(1)).create(request);
    Mockito.verify(responseHandler,Mockito.times(1)).process(Mockito.any(Response.class));
  }
  //Failing Test
  @Test
  public void givenRequest_shouldThrow() {
    // given
    Request request = new Request(1);
    Mockito.when(api.create(request))
        .thenReturn(
            Mono.error(new ClientException(new ErrorResponse("0101", "This is failed request"))));
    // when
    apiService.process(request);
    // then

    Mockito.verify(errorProcessor, Mockito.times(1)).process(Mockito.any(ClientException.class));
  }
}

我的测试方法givenRequest_shouldCreate正在验证一旦 API 响应到来,我的 ResponseHandler 就会被调用。

但是进入测​​试方法givenRequest_shouldThrow 的方式相同,我想验证一下,如果发生任何错误,那么我的 ErrorProcessor 将被调用

当我在测试上方运行时,出现以下错误

com.service.ClientException
    at com.service.APITest.givenRequest_shouldThrow(APITest.java:37)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
    Suppressed: java.lang.Exception: #block terminated with an error
        at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:93)
        at reactor.core.publisher.Mono.block(Mono.java:1663)
        at com.service.APIService.process(APIService.java:25)
        at com.service.APITest.givenRequest_shouldThrow(APITest.java:41)
        ... 63 more

所以我试图模拟的是,当我调用create方法时,它将返回Mono.error所以在 .doOnError 中我想保留错误。 所以我想确保我的errorHandler.saveError方法是否被调用。

有人可以帮助我吗

谢谢

阿尔佩什

您的测试应该期望process方法抛出异常,请参见下面的示例。 对于此线程中列出的异常,还有更多奇特的断言方法。

//Failing Test
@Test
public void givenRequest_shouldThrow() {
    // given
    Request request = new Request(1);
    Mockito.when(api.create(request))
            .thenReturn(
                    Mono.error(new ClientException(new ErrorResponse("0101", "This is failed request"))));
    // when
    try {
        apiService.process(request);
        fail("Process should have thrown an exception");
    } catch (ClientException e) {
        // then
        assertEquals("0101", e.getErrorResponse().errorCode);
        assertEquals("This is failed request", e.getErrorResponse().message);
        assertNull(e.getMessage());
        Mockito.verify(errorProcessor, Mockito.times(1)).process(Mockito.any(ClientException.class));
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM