javaretrofit2okhttpjunit5mockwebserver

Mocking nested retrofit api calls using MockWebServer


I am writing a junit test using okhttp3.mockwebserver for a retrofit2 rest api.

The trimmed down api looks like this:

public interface MyApi{
    @POST("/api/get-orders")
    retrofit2.Response<Set<String>> getOrders();

    @POST("/api/cxl-order")
    retrofit2.Response<String> cancelOrder(String ordeId);
}

The api is then injected to another class which delegates the calls thusly:

    public class MyExchange{
    
     private final MyApi api;
     
     public MyExchange(MyApi api){
          this.api = api;
     }
    
     public final Set<String> getOrders(){
         Response<Set<String>> resp = api.getOrders();
         //parse the response 
         Set<String> result = parse( resp );
         return result;
     }
    
    
     public final boolean cancelOrder( String orderId ){
       api.cancelOrder( orderId );
    
       //Nested Call
       Set<String> orders = getOrders();
       return !orders.contains(orderId);
    }
}

I do the following in my test:

 @Test
  public void cancel_order(){
      MockWebServer server = new MockWebServer();
      server.start();

      String orderId ="OrderId_123";
      MyApi mockApi = new Retrofit.Builder().baseUrl("/").build().create(MyApi.class);
      MyExchange exchange = new MyExchange(mockApi);

      server.enqueue( new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK, orderId));
      server.enqueue( new MockResponse().setResponseCode(HttpURLConnection.HTTP_OK, Set.of()));

      exchange.cancelOrder(orderId);
}    

Because the implementation of cancelOrder() calls api.cancelOrder() and then api.getOrders(), I added two mocked responses corresponding to each. However, looks like only the first mocked responses gets returned. For the second (getOrders), the mock server actually tries to connect over REST and then fails by timing out.

Any ideas as to how to mock responses for nested calls?

Cheers!


Solution

  • I ended up using the Dispatcher to check the path of the request. If the path ends in "get-orders", I send mocked response for Orders otherwise for cancel orders.

    Dispatcher dispatcher = (request) -> {
      if( request.getPath().endsWith("get-orders"){
         return mock response for orders
    
      }else if( request.getPath().endsWith("cxl-orders"){
         return mock response for cancel orders
      }
    }
    
    mockServer.setDispatcher(dispatcher);