2022-11-19 00:33:40 +08:00
|
|
|
[[spring-mvc-test-client]]
|
|
|
|
= Testing Client Applications
|
|
|
|
|
2025-06-06 22:28:18 +08:00
|
|
|
To test code that uses the `RestClient` or `RestTemplate`, you can use a mock web server, such as
|
|
|
|
https://github.com/square/okhttp#mockwebserver[OkHttp MockWebServer] or
|
|
|
|
https://wiremock.org/[WireMock]. Mock web servers accept requests over HTTP like a regular
|
|
|
|
server, and that means you can test with the same HTTP client that is also configured in
|
|
|
|
the same way as in production, which is important because there are often subtle
|
|
|
|
differences in the way different clients handle network I/O. Another advantage of mock
|
|
|
|
web servers is the ability to simulate specific network issues and conditions at the
|
|
|
|
transport level, in combination with the client used in production.
|
|
|
|
|
|
|
|
In addition to dedicated mock web servers, historically the Spring Framework has provided
|
|
|
|
a built-in option to test `RestClient` or `RestTemplate` through `MockRestServiceServer`.
|
|
|
|
This relies on configuring the client under test with a custom `ClientHttpRequestFactory`
|
|
|
|
backed by the mock server that is in turn set up to expect requests and send "`stub`"
|
|
|
|
responses so that you can focus on testing the code in isolation, without running a server.
|
|
|
|
|
|
|
|
TIP: `MockRestServiceServer` predates the existence of mock web servers. At present, we
|
|
|
|
recommend using mock web servers for more complete testing of the transport layer and
|
|
|
|
network conditions.
|
|
|
|
|
|
|
|
The following example shows an example of using `MockRestServiceServer`:
|
2022-11-19 00:33:40 +08:00
|
|
|
|
2023-04-21 05:21:36 +08:00
|
|
|
[tabs]
|
|
|
|
======
|
|
|
|
Java::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
|
|
|
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
|
|
|
|
Kotlin::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
val restTemplate = RestTemplate()
|
|
|
|
|
|
|
|
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
|
|
|
|
mockServer.expect(requestTo("/greeting")).andRespond(withSuccess())
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
|
|
|
|
mockServer.verify()
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
======
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
In the preceding example, `MockRestServiceServer` (the central class for client-side REST
|
|
|
|
tests) configures the `RestTemplate` with a custom `ClientHttpRequestFactory` that
|
|
|
|
asserts actual requests against expectations and returns "`stub`" responses. In this
|
|
|
|
case, we expect a request to `/greeting` and want to return a 200 response with
|
|
|
|
`text/plain` content. We can define additional expected requests and stub responses as
|
|
|
|
needed. When we define expected requests and stub responses, the `RestTemplate` can be
|
|
|
|
used in client-side code as usual. At the end of testing, `mockServer.verify()` can be
|
|
|
|
used to verify that all expectations have been satisfied.
|
|
|
|
|
|
|
|
By default, requests are expected in the order in which expectations were declared. You
|
|
|
|
can set the `ignoreExpectOrder` option when building the server, in which case all
|
|
|
|
expectations are checked (in order) to find a match for a given request. That means
|
|
|
|
requests are allowed to come in any order. The following example uses `ignoreExpectOrder`:
|
|
|
|
|
2023-04-21 05:21:36 +08:00
|
|
|
[tabs]
|
|
|
|
======
|
|
|
|
Java::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
|
|
|
|
Kotlin::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
======
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
Even with unordered requests by default, each request is allowed to run once only.
|
|
|
|
The `expect` method provides an overloaded variant that accepts an `ExpectedCount`
|
|
|
|
argument that specifies a count range (for example, `once`, `manyTimes`, `max`, `min`,
|
|
|
|
`between`, and so on). The following example uses `times`:
|
|
|
|
|
2023-04-21 05:21:36 +08:00
|
|
|
[tabs]
|
|
|
|
======
|
|
|
|
Java::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
|
|
|
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess());
|
|
|
|
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess());
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
|
|
|
|
Kotlin::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
val restTemplate = RestTemplate()
|
|
|
|
|
|
|
|
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
|
|
|
|
mockServer.expect(times(2), requestTo("/something")).andRespond(withSuccess())
|
|
|
|
mockServer.expect(times(3), requestTo("/somewhere")).andRespond(withSuccess())
|
|
|
|
|
|
|
|
// ...
|
|
|
|
|
|
|
|
mockServer.verify()
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
======
|
2022-11-19 00:33:40 +08:00
|
|
|
|
|
|
|
Note that, when `ignoreExpectOrder` is not set (the default), and, therefore, requests
|
|
|
|
are expected in order of declaration, then that order applies only to the first of any
|
|
|
|
expected request. For example if "/something" is expected two times followed by
|
|
|
|
"/somewhere" three times, then there should be a request to "/something" before there is
|
|
|
|
a request to "/somewhere", but, aside from that subsequent "/something" and "/somewhere",
|
|
|
|
requests can come at any time.
|
|
|
|
|
|
|
|
As an alternative to all of the above, the client-side test support also provides a
|
|
|
|
`ClientHttpRequestFactory` implementation that you can configure into a `RestTemplate` to
|
|
|
|
bind it to a `MockMvc` instance. That allows processing requests using actual server-side
|
|
|
|
logic but without running a server. The following example shows how to do so:
|
|
|
|
|
2023-04-21 05:21:36 +08:00
|
|
|
[tabs]
|
|
|
|
======
|
|
|
|
Java::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
|
|
|
|
this.restTemplate = new RestTemplate(new MockMvcClientHttpRequestFactory(mockMvc));
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
|
|
|
|
Kotlin::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
2022-11-19 00:33:40 +08:00
|
|
|
----
|
|
|
|
val mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build()
|
|
|
|
restTemplate = RestTemplate(MockMvcClientHttpRequestFactory(mockMvc))
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
======
|
2022-11-19 00:33:40 +08:00
|
|
|
|
2023-01-07 00:50:19 +08:00
|
|
|
In some cases it may be necessary to perform an actual call to a remote service instead
|
|
|
|
of mocking the response. The following example shows how to do that through
|
|
|
|
`ExecutingResponseCreator`:
|
2022-12-20 23:36:06 +08:00
|
|
|
|
2023-04-21 05:21:36 +08:00
|
|
|
[tabs]
|
|
|
|
======
|
|
|
|
Java::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
2022-12-20 23:36:06 +08:00
|
|
|
----
|
|
|
|
RestTemplate restTemplate = new RestTemplate();
|
|
|
|
|
2023-01-07 00:50:19 +08:00
|
|
|
// Create ExecutingResponseCreator with the original request factory
|
2022-12-20 23:36:06 +08:00
|
|
|
ExecutingResponseCreator withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory());
|
|
|
|
|
|
|
|
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
|
2023-01-07 00:50:19 +08:00
|
|
|
mockServer.expect(requestTo("/profile")).andRespond(withSuccess());
|
|
|
|
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse);
|
2022-12-20 23:36:06 +08:00
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
|
|
|
|
mockServer.verify();
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
|
|
|
|
Kotlin::
|
|
|
|
+
|
2024-09-08 23:20:10 +08:00
|
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
2022-12-20 23:36:06 +08:00
|
|
|
----
|
|
|
|
val restTemplate = RestTemplate()
|
|
|
|
|
2023-01-07 00:50:19 +08:00
|
|
|
// Create ExecutingResponseCreator with the original request factory
|
2022-12-20 23:36:06 +08:00
|
|
|
val withActualResponse = new ExecutingResponseCreator(restTemplate.getRequestFactory())
|
|
|
|
|
|
|
|
val mockServer = MockRestServiceServer.bindTo(restTemplate).build()
|
|
|
|
mockServer.expect(requestTo("/profile")).andRespond(withSuccess())
|
|
|
|
mockServer.expect(requestTo("/quoteOfTheDay")).andRespond(withActualResponse)
|
|
|
|
|
|
|
|
// Test code that uses the above RestTemplate ...
|
|
|
|
|
|
|
|
mockServer.verify()
|
|
|
|
----
|
2023-04-21 05:21:36 +08:00
|
|
|
======
|
2022-12-20 23:36:06 +08:00
|
|
|
|
|
|
|
In the preceding example, we create the `ExecutingResponseCreator` using the
|
|
|
|
`ClientHttpRequestFactory` from the `RestTemplate` _before_ `MockRestServiceServer` replaces
|
2023-01-07 00:50:19 +08:00
|
|
|
it with a different one that mocks responses.
|
|
|
|
Then we define expectations with two kinds of responses:
|
2022-12-20 23:36:06 +08:00
|
|
|
|
|
|
|
* a stub `200` response for the `/profile` endpoint (no actual request will be executed)
|
2023-01-07 00:50:19 +08:00
|
|
|
* a response obtained through a call to the `/quoteOfTheDay` endpoint
|
2022-12-20 23:36:06 +08:00
|
|
|
|
2023-01-07 00:50:19 +08:00
|
|
|
In the second case, the request is executed through the `ClientHttpRequestFactory` that was
|
2024-09-26 20:03:46 +08:00
|
|
|
captured earlier. This generates a response that could, for example, come from an actual remote server,
|
2023-01-07 00:50:19 +08:00
|
|
|
depending on how the `RestTemplate` was originally configured.
|
2022-12-20 23:36:06 +08:00
|
|
|
|
2022-11-19 00:33:40 +08:00
|
|
|
[[spring-mvc-test-client-static-imports]]
|
|
|
|
== Static Imports
|
|
|
|
|
|
|
|
As with server-side tests, the fluent API for client-side tests requires a few static
|
|
|
|
imports. Those are easy to find by searching for `MockRest*`. Eclipse users should add
|
|
|
|
`MockRestRequestMatchers.{asterisk}` and `MockRestResponseCreators.{asterisk}` as
|
|
|
|
"`favorite static members`" in the Eclipse preferences under Java -> Editor -> Content
|
|
|
|
Assist -> Favorites. That allows using content assist after typing the first character of
|
|
|
|
the static method name. Other IDEs (such IntelliJ) may not require any additional
|
|
|
|
configuration. Check for the support for code completion on static members.
|
|
|
|
|
|
|
|
[[spring-mvc-test-client-resources]]
|
|
|
|
== Further Examples of Client-side REST Tests
|
|
|
|
|
|
|
|
Spring MVC Test's own tests include
|
2023-11-21 22:59:24 +08:00
|
|
|
{spring-framework-code}/spring-test/src/test/java/org/springframework/test/web/client/samples[example
|
2022-11-19 00:33:40 +08:00
|
|
|
tests] of client-side REST tests.
|