当前位置: 首页 > 知识库问答 >
问题:

Spring Webflux如何将响应模拟为单声道。WebClient Junit出错

万俟浩
2023-03-14

我正在从事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;
  }
}

Response.java

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

负责人。Java语言

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

异常处理类

客户xception.java

public class ClientException extends RuntimeException {

  private ErrorResponse errorResponse;

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

  public ErrorResponse getErrorResponse() {
    return errorResponse;
  }
}

错误处理器

错误处理器。Java语言

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\u shouldCreate正在验证,一旦API响应到来,我的ResponseHandler就会被调用。

但以同样的方式进入测试方法givenRequest\u 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

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

有人能帮我吗

谢啦

阿尔卑斯

共有1个答案

云星波
2023-03-14

您的测试应该期望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));
    }
}
 类似资料:
  • 我有一个服务电话返回单声道。现在,在给用户提供API响应的同时,我想发送一些响应。我试过用flatMap和地图,但它不起作用。它给了我一个空的身体作为回应。 谢谢你

  • 我的理解是单声道 我说得对吗? 如果没有,单声道之间的区别是什么

  • 使用spring 5,对于reactor,我们有以下需求。 什么方法可以转换单声道

  • 我有一个方法,可以尝试使用WebClient返回Mono 它可以返回我期望的结果。然后我尝试创建另一个方法来支持列表作为参数 但这一次返回一个奇怪的结果。 我是反应式编程新手,将流和单声道结合起来,然后转换为流量的正确方法是什么?

  • 在我的views.py中,我有一个函数,它每次使用不同的响应来调用各种requests.get() 在我的测试类中,我想做这样的事情,但无法计算出确切的方法调用 步骤1: 验证响应包含“a response”、“b response”、“c response” 如何完成步骤1(模拟请求模块)?