title: Spring Webflux how to Mock response as Mono.error for WebClient Junit
body: <p>I am working on Spring WebFlux project,</p>
  
  <p>I am calling third party API to create entity using WebClient.</p>
  
  <p>I want to persist errors if WebClient is getting 4xx as reponse code.</p>
  
  <p>Below is the code which is requesting third party API</p>
  
  <p><strong>API.java</strong></p>
  
  <pre><code>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&lt;Response&gt; 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 -&gt; handleError(response))
          .bodyToMono(Response.class);
    }
  
    private Mono&lt;? extends ClientException&gt; handleError(ClientResponse response) {
      return response
          .bodyToMono(ErrorResponse.class)
          .map(errorResponse -&gt; new ClientException(errorResponse));
    }
  }
  </code></pre>
  
  <p>If server is returning 4xx response, then I am just converting it into a specif error response.</p>
  
  <p>Calling method is like below</p>
  
  <p><strong>APIService.java</strong></p>
  
  <pre><code>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 -&gt; responseHandler.process(response))
          .doOnError(
              throwable -&gt; {
                ClientException ce = (ClientException) throwable;
                errorProcessor.process(ce);
              })
          .block();
    }
  }
  </code></pre>
  
  <p><strong>Request.java</strong> </p>
  
  <pre><code>public class Request {
      final Map&lt;String, Integer&gt; data;
  
    public Request(int id) {
      data = new HashMap&lt;&gt;();
      data.put("counter", id);
    }
  
      public Map&lt;String, Integer&gt; getData() {
      return data;
    }
  }
  </code></pre>
  
  <p><strong>Response.java</strong></p>
  
  <pre><code>public class Response {
    int id;
    public Response(int id) {
      this.id = id;
    }
  }
  </code></pre>
  
  <p><strong>ResponseHandler.java</strong></p>
  
  <pre><code>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);
    }
  }
  </code></pre>
  
  <p>Exception Handing class</p>
  
  <p><strong>ClientException.java</strong></p>
  
  <pre><code>public class ClientException extends RuntimeException {
  
    private ErrorResponse errorResponse;
  
    public ClientException(ErrorResponse errorResponse) {
      this.errorResponse = errorResponse;
    }
  
    public ErrorResponse getErrorResponse() {
      return errorResponse;
    }
  }
  </code></pre>
  
  <p>Error Processor</p>
  
  <p><strong>ErrorProcessor.java</strong></p>
  
  <pre><code>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());
    }
  }
  </code></pre>
  
  <p>Error Response which is returned by API call</p>
  
  <p><strong>ErrorResponse.java</strong></p>
  
  <pre><code>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;
    }
  }
  </code></pre>
  
  <p>While Writing Junit as below</p>
  
  <p><strong>APITest.java</strong></p>
  
  <pre><code>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));
    }
  }
  </code></pre>
  
  <p>My test method <strong>givenRequest_shouldCreate</strong> is verifying that once API response is coming my ResponseHandler is getting called.</p>
  
  <p>But Same way into test method <strong>givenRequest_shouldThrow</strong>, I want to verify that, if any error occured then my ErrorProcessor is getting called</p>
  
  <p>When I ran above Test, I am getting following error</p>
  
  <pre><code>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
  </code></pre>
  
  <p>So What I am trying to mock is, When I invoke <strong>create</strong> method then it will return <strong>Mono.error</strong> so inside .doOnError I want to persist the error. So I want to make sure that my <strong>errorHandler.saveError</strong> method is invoked or not.</p>
  
  <p>Can some one please help me on this</p>
  
  <p>Thanks</p>
  
  <p>Alpesh </p>