Add support for using WebFlux's WebTestClient to document an API

Closes gh-384
This commit is contained in:
Andy Wilkinson
2017-10-30 09:40:24 +00:00
parent 1cd74a5c1d
commit cfb1fbc85d
52 changed files with 2948 additions and 58 deletions

View File

@@ -54,6 +54,12 @@ using the `RestDocumentationConfigurer` API. For example, to use `ISO-8859-1`:
include::{examples-dir}/com/example/mockmvc/CustomEncoding.java[tags=custom-encoding]
----
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/CustomEncoding.java[tags=custom-encoding]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -79,6 +85,12 @@ box. You can change the default format using the `RestDocumentationConfigurer` A
include::{examples-dir}/com/example/mockmvc/CustomFormat.java[tags=custom-format]
----
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/CustomFormat.java[tags=custom-format]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -109,6 +121,12 @@ snippet by default:
include::{examples-dir}/com/example/mockmvc/CustomDefaultSnippets.java[tags=custom-default-snippets]
----
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/CustomDefaultSnippets.java[tags=custom-default-snippets]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -130,6 +148,14 @@ include::{examples-dir}/com/example/mockmvc/CustomDefaultOperationPreprocessors.
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/CustomDefaultOperationPreprocessors.java[tags=custom-default-operation-preprocessors]
----
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.REST Assured
----

View File

@@ -18,6 +18,14 @@ include::{examples-dir}/com/example/mockmvc/PerTestPreprocessing.java[tags=prepr
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/PerTestPreprocessing.java[tags=preprocessing]
----
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -39,6 +47,14 @@ include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=set
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/EveryTestPreprocessing.java[tags=setup]
----
<1> Apply a request preprocessor that will remove the header named `Foo`.
<2> Apply a response preprocessor that will pretty print its content.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -55,6 +71,12 @@ Then, in each test, any configuration specific to that test can be performed. Fo
include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=use]
----
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/EveryTestPreprocessing.java[tags=use]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----

View File

@@ -23,6 +23,18 @@ include::{examples-dir}/com/example/mockmvc/Hypermedia.java[tag=links]
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
<3> Expect a link whose rel is `bravo`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Hypermedia.java[tag=links]
----
<1> Configure Spring REST docs to produce a snippet describing the response's links.
Uses the static `links` method on
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
<2> Expect a link whose rel is `alpha`. Uses the static `linkWithRel` method on
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
<3> Expect a link whose rel is `bravo`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -77,6 +89,14 @@ include::{examples-dir}/com/example/mockmvc/Hypermedia.java[tag=explicit-extract
<1> Indicate that the links are in HAL format. Uses the static `halLinks` method on
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Hypermedia.java[tag=explicit-extractor]
----
<1> Indicate that the links are in HAL format. Uses the static `halLinks` method on
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -152,6 +172,18 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=response]
on `org.springframework.restdocs.payload.PayloadDocumentation`.
<3> Expect a field with the path `contact.name`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=response]
----
<1> Configure Spring REST docs to produce a snippet describing the fields in the response
payload. To document a request `requestFields` can be used. Both are static methods on
`org.springframework.restdocs.payload.PayloadDocumentation`.
<2> Expect a field with the path `contact.email`. Uses the static `fieldWithPath` method
on `org.springframework.restdocs.payload.PayloadDocumentation`.
<3> Expect a field with the path `contact.name`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -181,7 +213,16 @@ subsection of a payload can be documented. For example:
include::{examples-dir}/com/example/mockmvc/Payload.java[tags=subsection]
----
<1> Document the subsection with the path `contact`. `contact.email` and `contact.name`
are now seen has having also been documented. Uses the static `subsectionWithPath`
are now seen as having also been documented. Uses the static `subsectionWithPath`
method on `org.springframework.restdocs.payload.PayloadDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=subsection]
----
<1> Document the subsection with the path `contact`. `contact.email` and `contact.name`
are now seen as having also been documented. Uses the static `subsectionWithPath`
method on `org.springframework.restdocs.payload.PayloadDocumentation`.
[source,java,indent=0,role="secondary"]
@@ -190,7 +231,7 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=subsection]
include::{examples-dir}/com/example/restassured/Payload.java[tags=subsection]
----
<1> Document the subsection with the path `contact`. `contact.email` and `contact.name`
are now seen has having also been documented. Uses the static `subsectionWithPath`
are now seen as having also been documented. Uses the static `subsectionWithPath`
method on `org.springframework.restdocs.payload.PayloadDocumentation`.
`subsectionWithPath` can be useful for providing a high-level overview of a particular
@@ -360,14 +401,21 @@ used:
----
include::{examples-dir}/com/example/mockmvc/Payload.java[tags=explicit-type]
----
<1> Set the field's type to `string`.
<1> Set the field's type to `String`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=explicit-type]
----
<1> Set the field's type to `String`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
include::{examples-dir}/com/example/restassured/Payload.java[tags=explicit-type]
----
<1> Set the field's type to `string`.
<1> Set the field's type to `String`.
[[documenting-your-api-request-response-payloads-fields-xml]]
@@ -443,6 +491,13 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=single-book]
----
<1> Document `title` and `author` using existing descriptors
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=single-book]
----
<1> Document `title` and `author` using existing descriptors
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -460,6 +515,14 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=book-array]
<1> Document the array
<2> Document `[].title` and `[].author` using the existing descriptors prefixed with `[].`
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=book-array]
----
<1> Document the array
<2> Document `[].title` and `[].author` using the existing descriptors prefixed with `[].`
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -508,6 +571,16 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=body-subsection]
`org.springframework.restdocs.payload.PayloadDocumentation`. To produce a snippet
for the request body, `requestBody` can be used in place of `responseBody`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=body-subsection]
----
<1> Produce a snippet containing a subsection of the response body. Uses the static
`responseBody` and `beneathPath` methods on
`org.springframework.restdocs.payload.PayloadDocumentation`. To produce a snippet
for the request body, `requestBody` can be used in place of `responseBody`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -548,7 +621,8 @@ This example will result in a snippet named `request-body-temp.adoc`.
===== Documenting the fields of a subsection of a request or response
As well as documenting a subsection of a request or response body, it's also possible to
document the fields in a particular subsection. A snippet that documents the fields of the `temperature` object (`high` and `low`) can be produced as follows:
document the fields in a particular subsection. A snippet that documents the fields of
the `temperature` object (`high` and `low`) can be produced as follows:
[source,java,indent=0,role="primary"]
.MockMvc
@@ -560,6 +634,16 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=fields-subsection]
`org.springframework.restdocs.payload.PayloadDocumentation`.
<2> Document the `high` and `low` fields.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=fields-subsection]
----
<1> Produce a snippet describing the fields in the subsection of the response payload
beneath the path `weather.temperature`. Uses the static `beneathPath` method on
`org.springframework.restdocs.payload.PayloadDocumentation`.
<2> Document the `high` and `low` fields.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -598,6 +682,20 @@ include::{examples-dir}/com/example/mockmvc/RequestParameters.java[tags=request-
`org.springframework.restdocs.request.RequestDocumentation`.
<4> Document the `per_page` parameter.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/RequestParameters.java[tags=request-parameters-query-string]
----
<1> Perform a `GET` request with two parameters, `page` and `per_page` in the query
string.
<2> Configure Spring REST Docs to produce a snippet describing the request's parameters.
Uses the static `requestParameters` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<3> Document the `page` parameter. Uses the static `parameterWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<4> Document the `per_page` parameter.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -621,6 +719,13 @@ include::{examples-dir}/com/example/mockmvc/RequestParameters.java[tags=request-
----
<1> Perform a `POST` request with a single parameter, `username`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/RequestParameters.java[tags=request-parameters-form-data]
----
<1> Perform a `POST` request with a single parameter, `username`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -667,6 +772,19 @@ include::{examples-dir}/com/example/mockmvc/PathParameters.java[tags=path-parame
`org.springframework.restdocs.request.RequestDocumentation`.
<4> Document the parameter named `longitude`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/PathParameters.java[tags=path-parameters]
----
<1> Perform a `GET` request with two path parameters, `latitude` and `longitude`.
<2> Configure Spring REST Docs to produce a snippet describing the request's path
parameters. Uses the static `pathParameters` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<3> Document the parameter named `latitude`. Uses the static `parameterWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<4> Document the parameter named `longitude`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -683,9 +801,9 @@ include::{examples-dir}/com/example/restassured/PathParameters.java[tags=path-pa
The result is a snippet named `path-parameters.adoc` that contains a table describing
the path parameters that are supported by the resource.
TIP: To make the path parameters available for documentation, the request must be
built using one of the methods on `RestDocumentationRequestBuilders` rather than
`MockMvcRequestBuilders`.
TIP: If you are using MockMvc then, to make the path parameters available for documentation,
the request must be built using one of the methods on `RestDocumentationRequestBuilders`
rather than `MockMvcRequestBuilders`.
When documenting path parameters, the test will fail if an undocumented path parameter
is used in the request. Similarly, the test will also fail if a documented path parameter
@@ -720,6 +838,18 @@ include::{examples-dir}/com/example/mockmvc/RequestParts.java[tags=request-parts
<3> Document the part named `file`. Uses the static `partWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/RequestParts.java[tags=request-parts]
----
<1> Perform a `POST` request with a single part named `file`.
<2> Configure Spring REST Docs to produce a snippet describing the request's parts. Uses
the static `requestParts` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<3> Document the part named `file`. Uses the static `partWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -775,6 +905,16 @@ include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=body]
`PayloadDocumentation`.
payload.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/RequestPartPayload.java[tags=body]
----
<1> Configure Spring REST docs to produce a snippet containing the body of the
of the request part named `metadata`. Uses the static `requestPartBody` method on
`PayloadDocumentation`.
payload.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -808,6 +948,18 @@ include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=fields]
<2> Expect a field with the path `version`. Uses the static `fieldWithPath` method on
`org.springframework.restdocs.payload.PayloadDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/RequestPartPayload.java[tags=fields]
----
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
of the request part named `metadata`. Uses the static `requestPartFields` method on
`PayloadDocumentation`.
payload.
<2> Expect a field with the path `version`. Uses the static `fieldWithPath` method on
`org.springframework.restdocs.payload.PayloadDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -863,6 +1015,20 @@ include::{examples-dir}/com/example/mockmvc/HttpHeaders.java[tags=headers]
<4> Produce a snippet describing the response's headers. Uses the static `responseHeaders`
method on `org.springframework.restdocs.headers.HeaderDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/HttpHeaders.java[tags=headers]
----
<1> Perform a `GET` request with an `Authorization` header that uses basic authentication
<2> Configure Spring REST Docs to produce a snippet describing the request's headers.
Uses the static `requestHeaders` method on
`org.springframework.restdocs.headers.HeaderDocumentation`.
<3> Document the `Authorization` header. Uses the static `headerWithName` method on
`org.springframework.restdocs.headers.HeaderDocumentation`.
<4> Produce a snippet describing the response's headers. Uses the static `responseHeaders`
method on `org.springframework.restdocs.headers.HeaderDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -910,6 +1076,14 @@ include::{examples-dir}/com/example/mockmvc/MockMvcSnippetReuse.java[tags=use]
<1> Reuse the `pagingLinks` `Snippet` calling `and` to add descriptors that are specific
to the resource that is being documented.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/WebTestClientSnippetReuse.java[tags=use]
----
<1> Reuse the `pagingLinks` `Snippet` calling `and` to add descriptors that are specific
to the resource that is being documented.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -1062,8 +1236,10 @@ You can configure which snippets are produced by default. Please refer to the
[[documentating-your-api-parameterized-output-directories]]
=== Using parameterized output directories
The output directory used by `document` can be parameterized. The following parameters
are supported:
When using MockMvc or REST Assured, the output directory used by `document` can be
parameterized. The output directory cannot be parameterized when using WebTestClient.
The following parameters are supported:
[cols="1,3"]
|===
@@ -1168,6 +1344,15 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=constraints]
<2> Set the `constraints` attribute for the `name` field
<3> Set the `constraints` attribute for the `email` field
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/Payload.java[tags=constraints]
----
<1> Configure the `title` attribute for the request fields snippet
<2> Set the `constraints` attribute for the `name` field
<3> Set the `constraints` attribute for the `email` field
[source,java,indent=0,role="secondary"]
.REST Assured
----

View File

@@ -28,6 +28,17 @@ If you want to jump straight in, a number of sample applications are available:
|===
[cols="3,2,10"]
.WebTestClient
|===
| Sample | Build system | Description
| {samples}/web-test-client[WebTestClient]
| Gradle
| Demonstrates the use of Spring REST docs with Spring WebFlux's WebTestClient.
|===
[cols="3,2,10"]
.REST Assured
@@ -129,8 +140,8 @@ the configuration are described below.
</build>
----
<1> Add a dependency on `spring-restdocs-mockmvc` in the `test` scope. If you want to use
REST Assured rather than MockMvc, add a dependency on `spring-restdocs-restassured`
instead.
`WebTestClient` or REST Assured rather than MockMvc, add a dependency on
`spring-restdocs-webtestclient` or `spring-restdocs-restassured` respectively instead.
<2> Add the Asciidoctor plugin.
<3> Using `prepare-package` allows the documentation to be
<<getting-started-build-configuration-maven-packaging, included in the package>>.
@@ -248,8 +259,9 @@ from where it will be included in the jar file.
[[getting-started-documentation-snippets]]
=== Generating documentation snippets
Spring REST Docs uses
{spring-framework-docs}/#spring-mvc-test-framework[Spring's MVC Test framework] or
Spring REST Docs uses Spring MVC's
{spring-framework-docs}/testing.html#spring-mvc-test-framework[test framework],
Spring WebFlux's {spring-framework-docs}/testing.html#webtestclient[`WebTestClient`] or
http://www.rest-assured.io[REST Assured] to make requests to the service that you are
documenting. It then produces documentation snippets for the request and the resulting
response.
@@ -301,7 +313,7 @@ The default can be overridden by providing an output directory when creating the
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("custom");
----
Next, provide an `@Before` method to configure MockMvc or REST Assured:
Next, provide an `@Before` method to configure MockMvc, WebTestClient or REST Assured:
[source,java,indent=0,role="primary"]
.MockMvc
@@ -312,6 +324,16 @@ include::{examples-dir}/com/example/mockmvc/ExampleApplicationTests.java[tags=se
instance of this class can be obtained from the static `documentationConfiguration()`
method on `org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/ExampleApplicationTests.java[tags=setup]
----
<1> The `WebTestClient` instance is configured by adding a
`WebTestclientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. An instance of
this class can be obtained from the static `documentationConfiguration()` method on
`org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -364,7 +386,7 @@ based on your project's build tool:
Next, provide an `@BeforeEach` method to configure MockMvc or REST Assured:
Next, provide a `@BeforeEach` method to configure MockMvc, WebTestClient, or REST Assured:
[source,java,indent=0,role="primary"]
.MockMvc
@@ -375,6 +397,16 @@ include::{examples-dir}/com/example/mockmvc/ExampleApplicationJUnit5Tests.java[t
instance of this class can be obtained from the static `documentationConfiguration()`
method on `org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/ExampleApplicationJUnit5Tests.java[tags=setup]
----
<1> The `WebTestClient` instance is configured by adding a
`WebTestClientRestDocumentationConfigurer` as an `ExchangeFilterFunction`. An instance of
this class can be obtained from the static `documentationConfiguration()` method on
`org.springframework.restdocs.webtestlcient.WebTestClientRestDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -407,7 +439,7 @@ private ManualRestDocumentation restDocumentation = new ManualRestDocumentation(
Secondly, `ManualRestDocumentation.beforeTest(Class, String)`
must be called before each test. This can be done as part of the method that is
configuring MockMVC or REST Assured:
configuring MockMvc, WebTestClient, or REST Assured:
[source,java,indent=0,role="primary"]
.MockMvc
@@ -415,6 +447,12 @@ configuring MockMVC or REST Assured:
include::{examples-dir}/com/example/mockmvc/ExampleApplicationTestNgTests.java[tags=setup]
----
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/ExampleApplicationTestNgTests.java[tags=setup]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----
@@ -449,6 +487,19 @@ a `RestDocumentationResultHandler`. An instance of this class can be obtained fr
static `document` method on
`org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`.
[source,java,indent=0,role="secondary"]
.WebTestClient
----
include::{examples-dir}/com/example/webtestclient/InvokeService.java[tags=invoke-service]
----
<1> Invoke the root (`/`) of the service and indicate that an `application/json` response
is required.
<2> Assert that the service produced the expected response.
<3> Document the call to the service, writing the snippets into a directory named `index`
that will be located beneath the configured output directory. The snippets are written by
a `Consumer` of the `ExchangeResult`. Such a consumer can be obtained from the static
`document` method on `org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation`.
[source,java,indent=0,role="secondary"]
.REST Assured
----

View File

@@ -13,7 +13,7 @@ Andy Wilkinson
:samples: {source}/samples
:templates: {source}spring-restdocs/src/main/resources/org/springframework/restdocs/templates
:spring-boot-docs: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle
:spring-framework-docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle
:spring-framework-docs: https://docs.spring.io/spring-framework/docs/5.0.x/spring-framework-reference
[[abstract]]

View File

@@ -10,8 +10,9 @@ http://asciidoctor.org[Asciidoctor] by default. Asciidoctor processes plain text
produces HTML, styled and layed out to suit your needs. If you prefer, Spring REST Docs
can also be configured to use Markdown.
Spring REST Docs makes use of snippets produced by tests written with
{spring-framework-docs}/#spring-mvc-test-framework[Spring MVC Test] or
Spring REST Docs makes use of snippets produced by tests written with Spring MVC's
{spring-framework-docs}/testing.html#spring-mvc-test-framework[test framework], Spring
WebFlux's {spring-framework-docs}/testing.html#webtestclient[`WebTestClient`] or
http://www.rest-assured.io[REST Assured 3]. This test-driven approach helps to guarantee
the accuracy of your service's documentation. If a snippet is incorrect the test that
produces it will fail.

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.removeHeaders;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class CustomDefaultOperationPreprocessors {
// @formatter:off
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private WebApplicationContext context;
@SuppressWarnings("unused")
private WebTestClient webTestClient;
@Before
public void setup() {
// tag::custom-default-operation-preprocessors[]
this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
.configureClient()
.filter(documentationConfiguration(this.restDocumentation)
.operationPreprocessors()
.withRequestDefaults(removeHeaders("Foo")) // <1>
.withResponseDefaults(prettyPrint())) // <2>
.build();
// end::custom-default-operation-preprocessors[]
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.cli.CliDocumentation.curlRequest;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class CustomDefaultSnippets {
// @formatter:off
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Autowired
private WebApplicationContext context;
@SuppressWarnings("unused")
private WebTestClient webTestClient;
@Before
public void setUp() {
// tag::custom-default-snippets[]
this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
.configureClient().filter(
documentationConfiguration(this.restDocumentation)
.snippets().withDefaults(curlRequest()))
.build();
// end::custom-default-snippets[]
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class CustomEncoding {
// @formatter:off
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Autowired
private WebApplicationContext context;
@SuppressWarnings("unused")
private WebTestClient webTestClient;
@Before
public void setUp() {
// tag::custom-encoding[]
this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient()
.filter(documentationConfiguration(this.restDocumentation)
.snippets().withEncoding("ISO-8859-1"))
.build();
// end::custom-encoding[]
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.restdocs.templates.TemplateFormats;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class CustomFormat {
// @formatter:off
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Autowired
private WebApplicationContext context;
@SuppressWarnings("unused")
private WebTestClient webTestClient;
@Before
public void setUp() {
// tag::custom-format[]
this.webTestClient = WebTestClient.bindToApplicationContext(this.context).configureClient()
.filter(documentationConfiguration(this.restDocumentation)
.snippets().withTemplateFormat(TemplateFormats.markdown()))
.build();
// end::custom-format[]
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
public class CustomUriConfiguration {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@Autowired
private WebApplicationContext context;
@SuppressWarnings("unused")
private MockMvc mockMvc;
@Before
public void setUp() {
// tag::custom-uri-configuration[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation).uris()
.withScheme("https")
.withHost("example.com")
.withPort(443))
.build();
// end::custom-uri-configuration[]
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.removeHeaders;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class EveryTestPreprocessing {
// @formatter:off
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
private WebApplicationContext context;
// tag::setup[]
private WebTestClient webTestClient;
@Before
public void setup() {
this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
.configureClient()
.filter(documentationConfiguration(this.restDocumentation)
.operationPreprocessors()
.withRequestDefaults(removeHeaders("Foo")) // <1>
.withResponseDefaults(prettyPrint())) // <2>
.build();
}
// end::setup[]
public void use() throws Exception {
// tag::use[]
this.webTestClient.get().uri("/").exchange().expectStatus().isOk()
.expectBody().consumeWith(document("index",
links(linkWithRel("self").description("Canonical self link"))));
// end::use[]
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
@ExtendWith(RestDocumentationExtension.class)
public class ExampleApplicationJUnit5Tests {
@SuppressWarnings("unused")
// tag::setup[]
private WebTestClient webTestClient;
@BeforeEach
public void setUp(WebApplicationContext webApplicationContext,
RestDocumentationContextProvider restDocumentation) {
this.webTestClient = WebTestClient.bindToApplicationContext(webApplicationContext)
.configureClient()
.filter(documentationConfiguration(restDocumentation)) // <1>
.build();
}
// end::setup[]
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import java.lang.reflect.Method;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.ManualRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class ExampleApplicationTestNgTests {
public final ManualRestDocumentation restDocumentation = new ManualRestDocumentation();
@SuppressWarnings("unused")
// tag::setup[]
private WebTestClient webTestClient;
@Autowired
private WebApplicationContext context;
@BeforeMethod
public void setUp(Method method) {
this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
.configureClient()
.filter(documentationConfiguration(this.restDocumentation)) // <1>
.build();
this.restDocumentation.beforeTest(getClass(), method.getName());
}
// end::setup[]
// tag::teardown[]
@AfterMethod
public void tearDown() {
this.restDocumentation.afterTest();
}
// end::teardown[]
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.documentationConfiguration;
public class ExampleApplicationTests {
@Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
@SuppressWarnings("unused")
// tag::setup[]
private WebTestClient webTestClient;
@Autowired
private WebApplicationContext context;
@Before
public void setUp() {
this.webTestClient = WebTestClient.bindToApplicationContext(this.context)
.configureClient()
.filter(documentationConfiguration(this.restDocumentation)) // <1>
.build();
}
// end::setup[]
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class HttpHeaders {
// @formatter:off
private WebTestClient webTestClient;
public void headers() throws Exception {
// tag::headers[]
this.webTestClient
.get().uri("/people").header("Authorization", "Basic dXNlcjpzZWNyZXQ=") // <1>
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("headers",
requestHeaders( // <2>
headerWithName("Authorization").description("Basic auth credentials")), // <3>
responseHeaders( // <4>
headerWithName("X-RateLimit-Limit")
.description("The total number of requests permitted per period"),
headerWithName("X-RateLimit-Remaining")
.description("Remaining requests permitted in current period"),
headerWithName("X-RateLimit-Reset")
.description("Time at which the rate limit period will reset"))));
// end::headers[]
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class Hypermedia {
private WebTestClient webTestClient;
public void defaultExtractor() throws Exception {
// tag::links[]
this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
.consumeWith(document("index",links( // <1>
linkWithRel("alpha").description("Link to the alpha resource"), // <2>
linkWithRel("bravo").description("Link to the bravo resource")))); // <3>
// end::links[]
}
public void explicitExtractor() throws Exception {
this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
// tag::explicit-extractor[]
.consumeWith(document("index",links(halLinks(), // <1>
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource"))));
// end::explicit-extractor[]
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class InvokeService {
private WebTestClient webTestClient;
public void invokeService() throws Exception {
// tag::invoke-service[]
this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON) // <1>
.exchange().expectStatus().isOk() // <2>
.expectBody().consumeWith(document("index")); // <3>
// end::invoke-service[]
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class PathParameters {
// @formatter:off
private WebTestClient webTestClient;
public void pathParametersSnippet() throws Exception {
// tag::path-parameters[]
this.webTestClient.get().uri("/locations/{latitude}/{longitude}", 51.5072, 0.1275) // <1>
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("locations",
pathParameters( // <2>
parameterWithName("latitude").description("The location's latitude"), // <3>
parameterWithName("longitude").description("The location's longitude")))); // <4>
// end::path-parameters[]
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class Payload {
// @formatter:off
private WebTestClient webTestClient;
public void response() throws Exception {
// tag::response[]
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("user",
responseFields( // <1>
fieldWithPath("contact.email").description("The user's email address"), // <2>
fieldWithPath("contact.name").description("The user's name")))); // <3>
// end::response[]
}
public void subsection() throws Exception {
// tag::subsection[]
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("user",
responseFields(
subsectionWithPath("contact").description("The user's contact details")))); // <1>
// end::subsection[]
}
public void explicitType() throws Exception {
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
// tag::explicit-type[]
.consumeWith(document("user",
responseFields(
fieldWithPath("contact.email")
.type(JsonFieldType.STRING) // <1>
.description("The user's email address"))));
// end::explicit-type[]
}
public void constraints() throws Exception {
this.webTestClient.get().uri("user/5").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
// tag::constraints[]
.consumeWith(document("create-user",
requestFields(
attributes(key("title").value("Fields for user creation")), // <1>
fieldWithPath("name")
.description("The user's name")
.attributes(key("constraints").value("Must not be null. Must not be empty")), // <2>
fieldWithPath("email")
.description("The user's email address")
.attributes(key("constraints").value("Must be a valid email address"))))); // <3>
// end::constraints[]
}
public void descriptorReuse() throws Exception {
FieldDescriptor[] book = new FieldDescriptor[] {
fieldWithPath("title").description("Title of the book"),
fieldWithPath("author").description("Author of the book") };
// tag::single-book[]
this.webTestClient.get().uri("/books/1").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("book",
responseFields(book))); // <1>
// end::single-book[]
// tag::book-array[]
this.webTestClient.get().uri("/books").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("books",
responseFields(
fieldWithPath("[]")
.description("An array of books")) // <1>
.andWithPrefix("[].", book))); // <2>
// end::book-array[]
}
public void fieldsSubsection() throws Exception {
// tag::fields-subsection[]
this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("temperature",
responseFields(beneathPath("weather.temperature"), // <1>
fieldWithPath("high").description("The forecast high in degrees celcius"), // <2>
fieldWithPath("low").description("The forecast low in degrees celcius"))));
// end::fields-subsection[]
}
public void bodySubsection() throws Exception {
// tag::body-subsection[]
this.webTestClient.get().uri("/locations/1").accept(MediaType.APPLICATION_JSON)
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("temperature",
responseBody(beneathPath("weather.temperature")))); // <1>
// end::body-subsection[]
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.removeHeaders;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class PerTestPreprocessing {
// @formatter:off
private WebTestClient webTestClient;
public void general() {
// tag::preprocessing[]
this.webTestClient.get().uri("/").exchange().expectStatus().isOk().expectBody()
.consumeWith(document("index",
preprocessRequest(removeHeaders("Foo")), // <1>
preprocessResponse(prettyPrint()))); // <2>
// end::preprocessing[]
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class RequestParameters {
// @formatter:off
private WebTestClient webTestClient;
public void getQueryStringSnippet() throws Exception {
// tag::request-parameters-query-string[]
this.webTestClient.get().uri("/users?page=2&per_page=100") // <1>
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("users", requestParameters( // <2>
parameterWithName("page").description("The page to retrieve"), // <3>
parameterWithName("per_page").description("Entries per page") // <4>
)));
// end::request-parameters-query-string[]
}
public void postFormDataSnippet() throws Exception {
// tag::request-parameters-form-data[]
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("username", "Tester");
this.webTestClient.post().uri("/users").body(BodyInserters.fromFormData(formData)) // <1>
.exchange().expectStatus().isCreated().expectBody()
.consumeWith(document("create-user", requestParameters(
parameterWithName("username").description("The user's username")
)));
// end::request-parameters-form-data[]
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import java.util.Collections;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class RequestPartPayload {
// @formatter:off
private WebTestClient webTestClient;
public void fields() throws Exception {
// tag::fields[]
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
Resource imageResource = new ByteArrayResource("<<png data>>".getBytes()) {
@Override
public String getFilename() {
return "image.png";
}
};
multipartData.add("image", imageResource);
multipartData.add("metadata", Collections.singletonMap("version", "1.0"));
this.webTestClient.post().uri("/images").syncBody(multipartData)
.accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
.consumeWith(document("image-upload",
requestPartFields("metadata", // <1>
fieldWithPath("version").description("The version of the image")))); // <2>
// end::fields[]
}
public void body() throws Exception {
// tag::body[]
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
Resource imageResource = new ByteArrayResource("<<png data>>".getBytes()) {
@Override
public String getFilename() {
return "image.png";
}
};
multipartData.add("image", imageResource);
multipartData.add("metadata", Collections.singletonMap("version", "1.0"));
this.webTestClient.post().uri("/images").syncBody(multipartData)
.accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
.consumeWith(document("image-upload",
requestPartBody("metadata"))); // <1>
// end::body[]
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class RequestParts {
// @formatter:off
private WebTestClient webTestClient;
public void upload() throws Exception {
// tag::request-parts[]
MultiValueMap<String, Object> multipartData = new LinkedMultiValueMap<>();
multipartData.add("file", "example".getBytes());
this.webTestClient.post().uri("/upload").syncBody(multipartData) // <1>
.exchange().expectStatus().isOk().expectBody()
.consumeWith(document("upload", requestParts( // <2>
partWithName("file").description("The file to upload")) // <3>
));
// end::request-parts[]
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.webtestclient;
import com.example.SnippetReuse;
import org.springframework.http.MediaType;
import org.springframework.test.web.reactive.server.WebTestClient;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation.document;
public class WebTestClientSnippetReuse extends SnippetReuse {
// @formatter:off
private WebTestClient webTestClient;
public void documentation() throws Exception {
// tag::use[]
this.webTestClient.get().uri("/").accept(MediaType.APPLICATION_JSON).exchange()
.expectStatus().isOk().expectBody()
.consumeWith(document("example", this.pagingLinks.and( // <1>
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource"))));
// end::use[]
}
}