Test Spring WebClient with MockWebServer from OkHttp

Last Updated:  August 27, 2021 | Published: June 28, 2020

In one of the last blog post, I demonstrated how to test the Spring RestTemplate with @RestClientTest. With this elegant solution, you can easily test parts of your application that use the RestTemplate and mock HTTP responses. Unfortunately, this test setup does not work for the Spring WebClient.  It seems there won't be an integration with the MockRestServiceServer for the WebClient. The recommended way is to use MockWebServer from OkHttp. With this blog post, you'll learn how to use the MockWebServer to test parts of your application using the Spring WebClient.

Spring WebClient with MockWebServer application setup

Our sample project is a basic Spring Boot application. We'll include both starters for Web and WebFlux. Hence Spring Boot autoconfigures the embedded Tomcat for us while we are able to use parts from Spring WebFlux like the WebClient.

Besides the Spring Boot Start Test, the project includes the MockWebServer dependency from OkHttp (Java HTTP Client library):

It's important to align the dependency version of the MockWebServer with the defined version of OkHttp by the Spring Boot Starter Parent. By default the Spring Boot 2.3 Parent references version 3.14.8 of the OkHttp client library.

Including a recent version of MockWebServer without overriding the OkHttp version, results in the following error:

That's why we override the version inside the properties section of our pom.xml and align both:

Spring WebClient usage

The following usage of the Spring WebClient should look familiar to you:

This client injects the autoconfigured WebClient.Builder and configures a WebClient with the correct base URL. We can now inject the UsersClient inside our application and make HTTP requests to fetch user data.

For using this in production, we can configure the actual URL inside our application.properties file:

It's important to make this URL configurable, as this will help us to test this part of the application. Furthermore, you also benefit as you can specify different URLs for your stages. Your sandbox environment might use a development endpoint of the external service.

Let's add a second method inside the UsersClient to test different scenarios later on:

This method ensures to successfully create a new user while verifying the response code. In case the external system returns any other HTTP status code than 201 (created), we'll throw an exception.

If you are new to the Spring WebClient and wonder why you should use it instead of the good old RestTemplate, consider reading this guide.

First JUnit 5 test with the MockWebServer

Let's write the first test using MockWebServer to verify the Spring WebClient can retrieve user data. The spawned server by MockWebServer is lightweight enough that we can create one server for each test method. This also ensures we won't have any side-effects from mocking HTTP responses in previous tests:

The @BeforeEach lifecycle method from JUnit 5 helps us to prepare everything for our actual test. Besides instantiating and starting the MockWebServer, we pass the URL to our UsersClient instance. It's important to create a new instance for our class under test (UsersClient here) for each new MockWebServer we start, as the mock server listens on a random ephemeral port.

Before we invoke the method of the UsersClient that we want to test, we have to prepare the response. Therefore we can construct a MockResponse matching our needs (body, HTTP header, response code) and queue it using mockWebServer.enqueue().

Compared to other HTTP response mocking solutions like WireMock, you queue your responses with MockWebServer. The local server will respond with the in the exact order you queue them. That's why we don't specify the actual path ("/users/1") our client will access and rely on the order of enqueued responses.

As an alternative, you can also use a Dispatcher, which we'll cover in of the next sections.

Finally, the MockWebServer provides a solution to verify the HTTP requests our application made during the test. Therefore we can request an instance of RecordedRequest and assert several parameters of the request (e.g. path, header, payload). If there are multiple requests during a test, simply invoke .takeRequest() to return the requests in the order they arrived at the server.

Further testing with Spring WebClient and MockWebServer

With a second test, we can ensure our client is able to create new users:

The setup is similar to the first test, but here we modify the response code of the server (default is 200).

With this test, I've also included a demo for another feature of MockWebServer: throttling responses. This is quite helpful when you want to test how your application behaves in case of a slow (or overloaded) external system.

Let's add a third test to verify our custom logic (verifying response code 201) is working.

Define responses for different requests with MockWebServer

In case the sequential ordering of mocked responses does not fit the use case you want to test, you can use a so-called Dispatcher.

This allows us to return responses based on any attribute of the request (header, path, body, etc.). For most of the cases the URL path is the most useful way to differentiate responses:

Here we return different response codes for different user endpoints. Compared to the other tests where we used .enqueue() to prepare the response, we use .setDispatcher() here and don't enqueue anything.

Summary of using MockWebServer for testing Spring WebClient

With this blog post, you should now be able to test the Spring WebClient parts of your application with MockWebServer.

The setup costs compared to the solution we have for testing the RestClient is negligible. Starting the MockWebServer is quite fast and the intuitive API makes the integration and usage straightforward. Compared to WireMock the feature set is more basic but good enough for most use cases.

You can find the sample application for this blog post on GitHub.

For more Spring WebClient related content, follow the links below:

Have fun testing your Spring WebClient with MockWebServer,

Phil

  • Great article, but I have one question.

    How can I mock third-party webclient requests while testing my controller with MockMvc?

    Basically, I have a Controller that takes data by using a WebClient in a Service, I want to mock the WebClient while testing my rest controller.

    Requests done with mockMvc are mocked too, unfortunately.

  • {"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
    >