Document AssertJ support for MockMvc

This commit restructures the section on MockMvc so that the anchors
are easier to read. The standard integration has moved to a
Hamcrest Integration section  at the same level as HtmlUnit Integration,
and a new AssertJ Integration section has been created.

Closes gh-32454
This commit is contained in:
Stéphane Nicoll
2024-06-21 12:50:36 +02:00
parent 7d236e29bb
commit d43dba63a1
54 changed files with 1432 additions and 272 deletions

View File

@@ -0,0 +1,49 @@
[[mockmvc-tester-assertions]]
= Defining Expectations
Assertions work the same way as any AssertJ assertions. The support provides dedicated
assert objects for the various pieces of the `MvcTestResult`, as shown in the following
example:
include-code::./HotelControllerTests[tag=get,indent=0]
If a request fails, the exchange does not throw the exception. Rather, you can assert
that the result of the exchange has failed:
include-code::./HotelControllerTests[tag=failure,indent=0]
The request could also fail unexpectedly, that is the exception thrown by the handler
has not been handled and is thrown as is. You can still use `.hasFailed()` and
`.failure()` but any attempt to access part of the result will throw an exception as
the exchange hasn't completed.
[[mockmvc-tester-assertions-json]]
== JSON Support
The AssertJ support for `MvcTestResult` provides JSON support via `bodyJson()`.
If https://github.com/jayway/JsonPath[JSONPath] is available, you can apply an expression
on the JSON document. The returned value provides convenient methods to return a dedicated
assert object for the various supported JSON data types:
include-code::./FamilyControllerTests[tag=extract-asmap,indent=0]
You can also convert the raw content to any of your data types as long as the message
converter is configured properly:
include-code::./FamilyControllerTests[tag=extract-convert,indent=0]
Converting to a target `Class` provides a generic assert object. For more complex types,
you may want to use `AssertFactory` instead that returns a dedicated assert type, if
possible:
include-code::./FamilyControllerTests[tag=extract-convert-assert-factory,indent=0]
https://jsonassert.skyscreamer.org[JSONAssert] is also supported. The body of the
response can be matched against a `Resource` or a content. If the content ends with
`.json ` we look for a file matching that name on the classpath:
include-code::./FamilyControllerTests[tag=assert-file,indent=0]
If you prefer to use another library, you can provide an implementation of
{spring-framework-api}/test/json/JsonComparator.html[`JsonComparator`].

View File

@@ -0,0 +1,23 @@
[[mockmvc-tester-integration]]
= MockMvc integration
If you want to use the AssertJ support but have invested in the original `MockMvc`
API, `MockMvcTester` offers several ways to integrate with it.
If you have your own `RequestBuilder` implementation, you can trigger the processing
of the request using `perform`. The example below showcases how the query can be
crafted with the original API:
include-code::./HotelControllerTests[tag=perform,indent=0]
Similarly, if you have crafted custom matchers that you use with the `.andExpect` feature
of `MockMvc` you can use them via `.matches`. In the example below, we rewrite the
preceding example to assert the status with the `ResultMatcher` implementation that
`MockMvc` provides:
include-code::./HotelControllerTests[tag=matches,indent=0]
`MockMvc` also defines a `ResultHandler` contract that lets you execute arbitrary actions
on `MvcResult`. If you have implemented this contract you can invoke it using `.apply`.

View File

@@ -0,0 +1,83 @@
[[mockmvc-tester-requests]]
= Performing Requests
This section shows how to use `MockMvcTester` to perform requests and its integration
with AssertJ to verify responses.
`MockMvcTester` provides a fluent API to compose the request that reuses the same
`MockHttpServletRequestBuilder` as the Hamcrest support, except that there is no need
to import a static method. The builder that is returned is AssertJ-aware so that
wrapping it in the regular `assertThat()` factory method triggers the exchange and
provides access to a dedicated Assert object for `MvcTestResult`.
Here is a simple example that performs a `POST` on `/hotels/42` and configures the
request to specify an `Accept` header:
include-code::./HotelControllerTests[tag=post,indent=0]
AssertJ often consists of multiple `assertThat()` statements to validate the different
parts of the exchange. Rather than having a single statement as in the case above, you
can use `.exchange()` to return a `MvcTestResult` that can be used in multiple
`assertThat` statements:
include-code::./HotelControllerTests[tag=post-exchange,indent=0]
You can specify query parameters in URI template style, as the following example shows:
include-code::./HotelControllerTests[tag=query-parameters,indent=0]
You can also add Servlet request parameters that represent either query or form
parameters, as the following example shows:
include-code::./HotelControllerTests[tag=parameters,indent=0]
If application code relies on Servlet request parameters and does not check the query
string explicitly (as is most often the case), it does not matter which option you use.
Keep in mind, however, that query parameters provided with the URI template are decoded
while request parameters provided through the `param(...)` method are expected to already
be decoded.
[[mockmvc-tester-requests-async]]
== Async
If the processing of the request is done asynchronously, `exchange()` waits for
the completion of the request so that the result to assert is effectively immutable.
The default timeout is 10 seconds but it can be controlled on a request-by-request
basis as shown in the following example:
include-code::./AsyncControllerTests[tag=duration,indent=0]
If you prefer to get the raw result and manage the lifecycle of the asynchronous
request yourself, use `asyncExchange` rather than `exchange`.
[[mockmvc-tester-requests-multipart]]
== Multipart
You can perform file upload requests that internally use
`MockMultipartHttpServletRequest` so that there is no actual parsing of a multipart
request. Rather, you have to set it up to be similar to the following example:
include-code::./MultipartControllerTests[tag=snippet,indent=0]
[[mockmvc-tester-requests-paths]]
== Using Servlet and Context Paths
In most cases, it is preferable to leave the context path and the Servlet path out of the
request URI. If you must test with the full request URI, be sure to set the `contextPath`
and `servletPath` accordingly so that request mappings work, as the following example
shows:
include-code::./HotelControllerTests[tag=context-servlet-paths,indent=0]
In the preceding example, it would be cumbersome to set the `contextPath` and
`servletPath` with every performed request. Instead, you can set up default request
properties, as the following example shows:
include-code::./HotelControllerTests[tag=default-customizations,indent=0]
The preceding properties affect every request performed through the `mockMvc` instance.
If the same property is also specified on a given request, it overrides the default
value. That is why the HTTP method and URI in the default request do not matter, since
they must be specified on every request.

View File

@@ -0,0 +1,30 @@
[[mockmvc-tester-setup]]
= Configuring MockMvcTester
`MockMvcTester` can be setup in one of two ways. One is to point directly to the
controllers you want to test and programmatically configure Spring MVC infrastructure.
The second is to point to Spring configuration with Spring MVC and controller
infrastructure in it.
TIP: For a comparison of those two modes, check xref:testing/mockmvc/setup-options.adoc[Setup Options].
To set up `MockMvcTester` for testing a specific controller, use the following:
include-code::./AccountControllerStandaloneTests[tag=snippet,indent=0]
To set up `MockMvcTester` through Spring configuration, use the following:
include-code::./AccountControllerIntegrationTests[tag=snippet,indent=0]
`MockMvcTester` can convert the JSON response body, or the result of a JSONPath expression,
to one of your domain object as long as the relevant `HttpMessageConverter` is registered.
If you use Jackson to serialize content to JSON, the following example registers the
converter:
include-code::./converter/AccountControllerIntegrationTests[tag=snippet,indent=0]
NOTE: The above assumes the converter has been registered as a Bean.
Finally, if you have a `MockMvc` instance handy, you can create a `MockMvcTester` by
providing the `MockMvc` instance to use using the `create` factory method.