Я работаю над проектом 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;
}
}
ResponseHandler. 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);
}
}
Класс обработки исключений
ClientException. java
public class ClientException extends RuntimeException {
private ErrorResponse errorResponse;
public ClientException(ErrorResponse errorResponse) {
this.errorResponse = errorResponse;
}
public ErrorResponse getErrorResponse() {
return errorResponse;
}
}
Ошибка процессора
ErrorProcessor. 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
ErrorResponse. 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 как показано ниже
APIT est. 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.
Но таким же образом в метод теста учитываяRequest_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 вызван или нет.
Может кто-нибудь помочь мне с этим
Спасибо
Alpe sh