Getting Started with MockWebServer and JUnit

The MockWebServer is a helpful library to mock dependent APIs on which the current component (under test) depends. Such mock APIs are extremely helpful in microservices architecture where we are developing multiple dependent services at the same time.

In this tutorial, we will learn to setup MockWebServer in JUnit 5 tests. We will be using Spring WebClient as HTTP client to invoke the mocked APIs.

1. Maven Dependencies

The MockWebServer is part of okhttp3 library so we need to import the following two dependencies.

<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>okhttp</artifactId>
   <version>4.10.0</version>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>com.squareup.okhttp3</groupId>
   <artifactId>mockwebserver</artifactId>
   <version>4.10.0</version>
   <scope>test</scope>
</dependency>

2. Starting and Stopping the MockWebServer

We can use the MockWebServer similar to other such libraries, such as WireMock. Typically, We

  • configure the mocks
  • start the server before tests begin
  • run the tests and verify the responses
  • stop the server after the end of the tests

The following example uses the @BeforeAll and @AfterAll hooks to start and stop the server. By default, the server starts in port 8080. We can use a different port by specifying in start() method.

public class MockWebServerTests {
  public static MockWebServer server;

  @BeforeAll
  static void setUp() throws IOException {
    server = new MockWebServer();
    server.start(8080);
  }

  @AfterAll
  static void tearDown() throws IOException {
    server.shutdown();
  }
}

If we want then we can create a new server instance for every unit test.

public void test() throws Exception {
   MockWebServer server = new MockWebServer();

   server.start(8080);

   //---test---

   server.shutdown();
}

3. Setting Up Mocks

3.1. Using Server.enqueue()

To setup mocks, we can use the Server.enqueue() method. We can enqueue as many responses as we want. Then we can hit the mocked API URL to fetch mocked responses in sequence.

public void test() throws Exception {
  MockWebServer server = new MockWebServer();

  server.enqueue(new MockResponse().setBody("message 1"));
  server.enqueue(new MockResponse().setBody("message 2"));
  server.enqueue(new MockResponse().setBody("message 3"));

  server.start();

  //more code
}

3.2. Using Dispatcher

Returning the enqueued responses is not suitable in all conditions. We can use Dispatcher class to devise our own logic to return API responses.

In the following example, we are using a dispatcher to match the API URI of the incoming request and then return the response for a matching URI.

public class MockWebServerTests {
  public static MockWebServer server;

  final static Dispatcher dispatcher = new Dispatcher() {

    @Override
    public MockResponse dispatch(RecordedRequest request) throws InterruptedException {

      switch (request.getPath()) {
        case "/api-url-one":
          return new MockResponse()
              .setResponseCode(201);

        case "/api-url-two":
          return new MockResponse()
              .setHeader("x-header-name", "header-value")
              .setResponseCode(200)
              .setBody("<response />");

        case "/api-url-three":
          return new MockResponse()
              .setResponseCode(500)
              .setBodyDelay(5000, TimeUnit.SECONDS)
              .setChunkedBody("<error-response />", 5);

        case "/api-url-four":
          return new MockResponse()
              .setResponseCode(200)
              .setBody("{\"data\":\"\"}")
              .throttleBody(1024, 5, TimeUnit.SECONDS);
      }
      return new MockResponse().setResponseCode(404);
    }
  };

  @BeforeAll
  static void setUp() throws IOException {
    server = new MockWebServer();
    server.setDispatcher(dispatcher);
    server.start(8080);
  }

  //more test code
}

4. Writing Tests

4.1. Normal JUnit Tests

After setting up the mocks, we can hit the mock APIs using Spring WebClient. To get the API host URL, use server.getHostName() method.

WebClient webClient = WebClient
        .create(String.format("http://%s:8080", server.getHostName()));

Finally, hit the mock API and pass on the request parameters and body, as necessary.

Mono<String> apiResponse = webClient.post()
  .uri("/api-url-two")
  .body(Mono.just("<data />"), String.class)
  .header("Authorization", "Basic " +
      Base64Utils.encodeToString(("username:password").getBytes(UTF_8)))
  .retrieve()
  .bodyToMono(String.class);

Once the API response is available, we can project Reactor’s StepVerifier to test these async responses.

StepVerifier.create(apiResponse)
  .expectNext("<response />")
  .verifyComplete();

4.2. Error Conditions

API responses will not be successful all the time. we may get different error codes and other fails such as network issues and latencies. MockWebServer supports these kinds of erroneous mock responses.

For example, we can test the timeout logic and delayed responses using setBodyDelay() method.

new MockResponse()
     .setResponseCode(200)
     .setBodyDelay(5000, TimeUnit.MILLISECONDS)
     .setBody("<data-response />");

To test a slow network, we can use setChunkedBody() method to send the response in chunks. The given mock will send the response in 5 chunks.

new MockResponse()
     .setResponseCode(200)
     .setChunkedBody("<data-response />", 5);

5. Verifying Server Stats

Sometimes it is important to verify how many times a request was hit on the mock server. This is especially useful when we are implementing and testing the retry logic. We can use RecordedRequest instance to fetch the details of HTTP requests MockWebServer to make sure our WebClient sent it correctly.

RecordedRequest request = server.takeRequest();

assertEquals("/api-url-two", request.getPath());
assertEquals("POST", request.getMethod());
assertNotNull(request.getHeader("Authorization"));
assertEquals("<data />", request.getBody().readUtf8());

6. Conclusion

In this tutorial, we learned to use MockWebServer to mock APIs and responses and later consume these API using WebClient.

We learned to start and stop the server, setup mocks, write success and error tests, verify the details of sent requests etc. There are some other popular alternatives, you can consider, such as WireMock.

Happy Learning !!

Source Code on Github

Was this post helpful?

Join 8000+ Awesome Developers, Like YOU!

Leave a Comment

About HowToDoInJava

This blog provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.