I am working on Spring WebFlux project,
I am calling third party API to create entity using WebClient.
I want to persist errors if WebClient is getting 4xx as reponse code.
Below is the code which is requesting third party 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));
}
}
If server is returning 4xx response, then I am just converting it into a specif error response.
Calling method is like below
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();
}
}
Request.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;
}
}
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);
}
}
Exception Handing class
ClientException.java
public class ClientException extends RuntimeException {
private ErrorResponse errorResponse;
public ClientException(ErrorResponse errorResponse) {
this.errorResponse = errorResponse;
}
public ErrorResponse getErrorResponse() {
return errorResponse;
}
}
Error Processor
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());
}
}
Error Response which is returned by API call
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;
}
}
While Writing Junit as below
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));
}
}
My test method givenRequest_shouldCreate is verifying that once API response is coming my ResponseHandler is getting called.
But Same way into test method givenRequest_shouldThrow , I want to verify that, if any error occured then my ErrorProcessor is getting called
When I ran above Test, I am getting following error
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
So What I am trying to mock is, When I invoke create method then it will return Mono.error so inside .doOnError I want to persist the error. So I want to make sure that my errorHandler.saveError method is invoked or not.
Can some one please help me on this
Thanks
Alpesh
Your test should expect the process
method to throw the exception, see example below. There are more fancy ways of asserting for exceptions listed in this thread .
//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));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.