[Spring Boot] Unit Testing Functional Endpoints and Service with API calls using Spring WebFlux
This article shows a few ways of writing unit tests to test drive the controller and service layers of a Reactive application.
How to test Functional Endpoints
Functional endpoints can be mapped by defining a RouterFunction
bean. Let us look at an example below which maps 4 endpoints. It can be observed that each endpoint is mapped with HandlerFunction
(userHandler) in the example. Also, the order of the endpoints matters. For example, if /{id}
endpoint is declared before /posts
, whenever /posts
is called, it parses “posts” as the id and it will trigger userHandler.getUser("posts")
instead of userHandler.getPostsByUsers()
.
For each HandlerFunction
, it takes in ServerRequest
as a parameter and returns a Mono<ServerResponse>
.
The mapped endpoints can be tested with WebTestClient
. When building a WebTestClient
, it can bind to a specific RouterFunction
.
How to test Services which use WebClient
When implementing business logic, there will be times where an external API is required. WebClient
is used to invoke the APIs. However, WebClient is part of the logic and it is important for the test code to have to control over the behavior and the return values of the external API invoked with WebClient.
Imagine the service implementation looks like below. It can be noticed that fetchUser(id)
calls and returns the result of the API (/users/{id}
) from another service.
In order to test UserService
, there will be two tests. Firstly, by calling fetchUser
, a call needs to be made to a specific endpoint (somehow a request needs to be captured to verify). Secondly, the return value of the fetchUser
is what we expected (the return value of the API needs to be stubbed).
For these purposes, mockwebserver3
will be used. Add the following dependencies to pom.xml
.
Set up and tear down of the MockWebServer
can be done like this:
Capturing the Sent Request
The test below is to verify that calling fetchUser
will invoke GET /users/1
endpoint. The sent request can be captured with MockWebServer.takeRequest()
.
Stubbing the return value of the WebClient
The test case below is to verify that fetchUser
returns the expected values. The return value of the API can be stubbed with MockWebServer.enqueue()
.
Full Source can be found at: https://github.com/jskim1991/spring-boot-webclient-sample