mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
aac521c116
This commit adds integration tests and reference documentation for multipart support in `RestClient` and `RestTestClient`. Closes gh-35569 Closes gh-33263
243 lines
6.9 KiB
Plaintext
243 lines
6.9 KiB
Plaintext
[[resttestclient]]
|
|
= RestTestClient
|
|
|
|
`RestTestClient` is an HTTP client designed for testing server applications. It wraps
|
|
Spring's xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] and uses it to perform requests,
|
|
but exposes a testing facade for verifying responses. `RestTestClient` can be used to
|
|
perform end-to-end HTTP tests. It can also be used to test Spring MVC
|
|
applications without a running server via MockMvc.
|
|
|
|
|
|
|
|
|
|
[[resttestclient.setup]]
|
|
== Setup
|
|
|
|
To set up a `RestTestClient` you need to choose a server setup to bind to. This can be one
|
|
of several MockMvc setup choices, or a connection to a live server.
|
|
|
|
|
|
|
|
[[resttestclient.controller-config]]
|
|
=== Bind to Controller
|
|
|
|
This setup allows you to test specific controller(s) via mock request and response objects,
|
|
without a running server.
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
RestTestClient client =
|
|
RestTestClient.bindToController(new TestController()).build();
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val client = RestTestClient.bindToController(TestController()).build()
|
|
----
|
|
======
|
|
|
|
[[resttestclient.context-config]]
|
|
=== Bind to `ApplicationContext`
|
|
|
|
This setup allows you to load Spring configuration with Spring MVC
|
|
infrastructure and controller declarations and use it to handle requests via mock request
|
|
and response objects, without a running server.
|
|
|
|
include-code::./RestClientContextTests[indent=0]
|
|
|
|
|
|
[[resttestclient.fn-config]]
|
|
=== Bind to Router Function
|
|
|
|
This setup allows you to test xref:web/webmvc-functional.adoc[functional endpoints] via
|
|
mock request and response objects, without a running server.
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
RouterFunction<?> route = ...
|
|
client = RestTestClient.bindToRouterFunction(route).build();
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
val route: RouterFunction<*> = ...
|
|
val client = RestTestClient.bindToRouterFunction(route).build()
|
|
----
|
|
======
|
|
|
|
[[resttestclient.server-config]]
|
|
=== Bind to Server
|
|
|
|
This setup connects to a running server to perform full, end-to-end HTTP tests:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
client = RestTestClient.bindToServer().baseUrl("http://localhost:8080").build();
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
client = RestTestClient.bindToServer().baseUrl("http://localhost:8080").build()
|
|
----
|
|
======
|
|
|
|
|
|
|
|
[[resttestclient.client-config]]
|
|
=== Client Config
|
|
|
|
In addition to the server setup options described earlier, you can also configure client
|
|
options, including base URL, default headers, client filters, and others. These options
|
|
are readily available following the initial `bindTo` call, as follows:
|
|
|
|
[tabs]
|
|
======
|
|
Java::
|
|
+
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
client = RestTestClient.bindToController(new TestController())
|
|
.baseUrl("/test")
|
|
.build();
|
|
----
|
|
|
|
Kotlin::
|
|
+
|
|
[source,kotlin,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
client = RestTestClient.bindToController(TestController())
|
|
.baseUrl("/test")
|
|
.build()
|
|
----
|
|
======
|
|
|
|
|
|
|
|
|
|
[[resttestclient.tests]]
|
|
== Writing Tests
|
|
|
|
xref:integration/rest-clients.adoc#rest-restclient[`RestClient`] and `RestTestClient` have
|
|
the same API up to the point of the call to `exchange()`. After that, `RestTestClient`
|
|
provides two alternative ways to verify the response:
|
|
|
|
1. xref:resttestclient-workflow[Built-in Assertions] extend the request workflow with a chain of expectations
|
|
2. xref:resttestclient-assertj[AssertJ Integration] to verify the response via `assertThat()` statements
|
|
|
|
TIP: See the xref:integration/rest-clients.adoc#rest-message-conversion[HTTP Message Conversion]
|
|
section for examples on how to prepare a request with any content, including form data and multipart data.
|
|
|
|
|
|
|
|
[[resttestclient.workflow]]
|
|
=== Built-in Assertions
|
|
|
|
To use the built-in assertions, remain in the workflow after the call to `exchange()`, and
|
|
use one of the expectation methods. For example:
|
|
|
|
include-code::./RestClientWorkflowTests[tag=test,indent=0]
|
|
|
|
|
|
If you would like for all expectations to be asserted even if one of them fails, you can
|
|
use `expectAll(..)` instead of multiple chained `expect*(..)` calls. This feature is
|
|
similar to the _soft assertions_ support in AssertJ and the `assertAll()` support in
|
|
JUnit Jupiter.
|
|
|
|
include-code::./RestClientWorkflowTests[tag=soft-assertions,indent=0]
|
|
|
|
|
|
You can then choose to decode the response body through one of the following:
|
|
|
|
* `expectBody(Class<T>)`: Decode to single object.
|
|
* `expectBody()`: Decode to `byte[]` for xref:testing/resttestclient.adoc#resttestclient-json[JSON Content] or an empty body.
|
|
|
|
|
|
If the built-in assertions are insufficient, you can consume the object instead and
|
|
perform any other assertions:
|
|
|
|
include-code::./RestClientWorkflowTests[tag=consume,indent=0]
|
|
|
|
Or you can exit the workflow and obtain a `EntityExchangeResult`:
|
|
|
|
include-code::./RestClientWorkflowTests[tag=result,indent=0]
|
|
|
|
|
|
TIP: When you need to decode to a target type with generics, look for the overloaded methods
|
|
that accept {spring-framework-api}/core/ParameterizedTypeReference.html[`ParameterizedTypeReference`]
|
|
instead of `Class<T>`.
|
|
|
|
|
|
[[resttestclient.no-content]]
|
|
==== No Content
|
|
|
|
If the response is not expected to have content, you can assert that as follows:
|
|
|
|
include-code::./NoContentTests[tag=emptyBody,indent=0]
|
|
|
|
If you want to ignore the response content, the following releases the content without any assertions:
|
|
|
|
include-code::./NoContentTests[tag=ignoreBody,indent=0]
|
|
|
|
NOTE: Consuming the response body (for example, with `expectBody`) is required if your tests are running with
|
|
leak detection for pooled buffers. Without that, the tool will report buffers being leaked.
|
|
|
|
|
|
[[resttestclient.json]]
|
|
==== JSON Content
|
|
|
|
You can use `expectBody()` without a target type to perform assertions on the raw
|
|
content rather than through higher level Object(s).
|
|
|
|
To verify the full JSON content with https://jsonassert.skyscreamer.org[JSONAssert]:
|
|
|
|
include-code::./JsonTests[tag=jsonBody,indent=0]
|
|
|
|
|
|
To verify JSON content with https://github.com/jayway/JsonPath[JSONPath]:
|
|
|
|
include-code::./JsonTests[tag=jsonPath,indent=0]
|
|
|
|
|
|
[[resttestclient.multipart]]
|
|
==== Multipart Content
|
|
|
|
When testing endpoints that return multipart responses, you can decode the body to a
|
|
`MultiValueMap<String, Part>` and assert individual parts using the `FormFieldPart`
|
|
and `FilePart` subtypes.
|
|
|
|
include-code::./MultipartTests[tag=multipart,indent=0]
|
|
|
|
|
|
[[resttestclient.assertj]]
|
|
=== AssertJ Integration
|
|
|
|
`RestTestClientResponse` is the main entry point for the AssertJ integration.
|
|
It is an `AssertProvider` that wraps the `ResponseSpec` of an exchange in order to enable
|
|
use of `assertThat()` statements. For example:
|
|
|
|
include-code::./AssertJTests[tag=withSpec,indent=0]
|
|
|
|
|
|
You can also use the built-in workflow first, and then obtain an `ExchangeResult` to wrap
|
|
and continue with AssertJ. For example:
|
|
|
|
include-code::./AssertJTests[tag=withResult,indent=0]
|