spring-framework/framework-docs/modules/ROOT/pages/testing/spring-mvc-test-client.adoc

232 lines
8.7 KiB
Plaintext
Raw Normal View History

[[spring-mvc-test-client]]
= Testing Client Applications
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`:
2023-04-21 05:21:36 +08:00
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
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::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
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
======
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::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build();
----
2023-04-21 05:21:36 +08:00
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder(true).build()
----
2023-04-21 05:21:36 +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::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
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::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
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
======
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::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
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::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
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
======
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`:
2023-04-21 05:21:36 +08:00
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes"]
----
RestTemplate restTemplate = new RestTemplate();
2023-01-07 00:50:19 +08:00
// Create ExecutingResponseCreator with the original request factory
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);
// Test code that uses the above RestTemplate ...
mockServer.verify();
----
2023-04-21 05:21:36 +08:00
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes"]
----
val restTemplate = RestTemplate()
2023-01-07 00:50:19 +08:00
// Create ExecutingResponseCreator with the original request factory
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
======
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:
* 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
2023-01-07 00:50:19 +08:00
In the second case, the request is executed through the `ClientHttpRequestFactory` that was
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.
[[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
{spring-framework-code}/spring-test/src/test/java/org/springframework/test/web/client/samples[example
tests] of client-side REST tests.