Isolate and reduce Spring Test dependencies

This commit splits Spring REST Docs into two projects –
spring-restdocs-core and spring-restdocs-mockmvc.

spring-restdocs-core contains the vast majority of the code but does not
depend on a specific test framework other than JUnit. The use of a
Spring Test TestExecutionListener has been replaced with a JUnit test
rule. The rule is declared once per test class and configured with
the output directory to which the generated snippets should be written.
This simplifies the implementation as thread local storage is no longer
required to transfer information about the test that’s running into
Spring REST Docs. Instead, this transfer is now handled by the new test
rule. It has also simplified the configuration as it’s no longer
necessary for users to provide a system property that configures the
output directory.

spring-restdocs-mockmvc contains code that’s specific to using Spring
REST Docs with Spring MVC Test’s MockMvc. This is currently the only
testing framework that’s supported, but it paves the way for adding
support for additional frameworks. REST Assured is one that users seem
particularly interested in (see gh-80 and gh-102).

Closes gh-107
This commit is contained in:
Andy Wilkinson
2015-09-02 16:20:34 +01:00
parent 9d8bbf0558
commit 2b2b6fcd25
207 changed files with 1396 additions and 1425 deletions

View File

@@ -64,24 +64,4 @@ by default:
[source,java,indent=0]
----
include::{examples-dir}/com/example/CustomDefaultSnippetsConfiguration.java[tags=custom-default-snippets]
----
[[configuration-output-directory]]
=== Snippet output directory
As described in <<getting-started-build-configuration>> the snippet output directory is
configured in your `pom.xml` or `build.gradle` file. This configuration applies to builds
on the command line, but it may not apply when running your tests in your IDE. In the
absence of the property, Spring REST Docs will write the generated snippets to standard
out.
If you'd prefer that your IDE writes the snippets to disk you can use a file in
`src/test/resources` named `documentation.properties` to specify the output directory that
should be used:
[source,properties]
----
org.springframework.restdocs.outputDir: target/generated-snippets
----

View File

@@ -36,10 +36,9 @@ The first step in using Spring REST Docs is to configure your project's build.
[[getting-started-build-configuration-gradle]]
==== Gradle build configuration
Both {samples}[sample applications] contain `build.gradle` files that you may wish to
use as a reference. The key parts of the configuration are described below.
The {samples/rest-notes-spring-hateoas}[Spring HATEOAS sample] contains a `build.gradle`
file that you may wish to use as a reference. The key parts of the configuration are
described below.
[source,groovy,indent=0,subs="verbatim,attributes"]
----
@@ -48,7 +47,7 @@ use as a reference. The key parts of the configuration are described below.
}
dependencies { <2>
testCompile 'org.springframework.restdocs:spring-restdocs:{project-version}'
testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc:{project-version}'
}
ext { <3>
@@ -56,24 +55,25 @@ use as a reference. The key parts of the configuration are described below.
}
test { <4>
systemProperty 'org.springframework.restdocs.outputDir', snippetsDir
outputs.dir snippetsDir
}
asciidoctor { <5>
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
attributes 'snippets': snippetsDir <6>
inputs.dir snippetsDir <7>
dependsOn test <8>
}
----
<1> Apply the Asciidoctor plugin.
<2> Add a dependency on spring-restdocs in the `testCompile` configuration.
<3> Configure a property to define the output location for generated snippets.
<4> Configure the `test` task with the `org.springframework.restdocs.outputDir` system
property. This property controls the location into which Spring REST Docs will write the
snippets that it generates.
<5> Configure the `asciidoctor` task and define an attribute named `snippets`. You can
then use this attribute when including the generated snippets in your documentation.
<4> Configure the `test` task to add the snippets directory as an output.
<5> Configure the `asciidoctor` task
<6> Define an attribute named `snippets` that can be used when including the generated
snippets in your documentation.
<7> Configure the snippets directory as an input.
<8> Make the task depend on the test task so that the tests are run before the
documentation is created.
[[getting-started-build-configuration-gradle-packaging-the-documentation]]
@@ -100,16 +100,15 @@ directory:
[[getting-started-build-configuration-maven]]
==== Maven build configuration
Both {samples}[sample applications] contain `pom.xml` files that you may wish to
use as a reference. The key parts of the configuration are described below.
The {samples/rest-notes-spring-data-rest}[Spring Data REST sample] contains a `pom.xml`
file that you may wish to use as a reference. The key parts of the configuration are
described below.
[source,xml,indent=0,subs="verbatim,attributes"]
----
<dependency> <1>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs</artifactId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>{project-version}</version>
<scope>test</scope>
</dependency>
@@ -127,11 +126,6 @@ use as a reference. The key parts of the configuration are described below.
<includes>
<include>**/*Documentation.java</include>
</includes>
<systemPropertyVariables>
<org.springframework.restdocs.outputDir>
${snippetsDirectory}
</org.springframework.restdocs.outputDir>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin> <4>
@@ -159,14 +153,12 @@ use as a reference. The key parts of the configuration are described below.
</build>
----
<1> Add a dependency on `spring-restdocs` in the `test` scope.
<1> Add a dependency on `spring-restdocs-mockmvc` in the `test` scope.
<2> Configure a property to define the output location for generated snippets.
<3> Configure the SureFire plugin with the `org.springframework.restdocs.outputDir` system
property. This property controls the location into which Spring REST Docs will write the
snippets that it generates. The plugin is also configured to include files whose names end
with `Documentation.java`.
<4> Configure the Asciidoctor plugin and define an attribute named `snippets`. You can
then use this attribute when including the generated snippets in your documentation.
<3> Add the SureFire plugin and configure it to include files whose names end with
`Documentation.java`.
<4> Add the Asciidoctor plugin and configure it to define an attribute named `snippets`
that can be used when including the generated snippets in your documentation.
<5> [[getting-started-build-configuration-maven-plugin-phase]] If you want to
<<getting-started-build-configuration-maven-packaging, package the documentation>> in your
project's jar you should use the `prepare-package` phase.
@@ -233,7 +225,8 @@ documentation snippets for the result's request and response.
[[getting-started-documentation-snippets-setup]]
==== Setting up Spring MVC test
The first step in generating documentation snippets is to provide an `@Before` method
The first step in generating documentation snippets is to declare a `public`
`RestDocumentation` that's annotated as a JUnit `@Rule` and to provide an `@Before` method
that creates a `MockMvc` instance:
[source,java,indent=0]
@@ -241,6 +234,10 @@ that creates a `MockMvc` instance:
include::{examples-dir}/com/example/ExampleApplicationTests.java[tags=mock-mvc-setup]
----
The `RestDocumentation` rule is configured with the output directory into which
generated snippets should be written. This output directory should match the snippets
directory that you have configured in your `build.gradle` or `pom.xml` file.
The `MockMvc` instance is configured using a `RestDocumentationConfigurer`. An instance
of this class can be obtained from the static `documentationConfiguration()` method on
`org.springframework.restdocs.RestDocumentation`. `RestDocumentationConfigurer` applies
@@ -259,13 +256,14 @@ service and document the request and response.
----
include::{examples-dir}/com/example/InvokeService.java[tags=invoke-service]
----
<1> Invoke the root (`/`) of the service an indicate that an `application/json` response
<1> Invoke the root (`/`) of the service and indicate that an `application/json` response
is required.
<2> Assert that the service is produced the expected response.
<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 `RestDocumentationResultHandler`. An instance of this class can be obtained from the
static `document` method on `org.springframework.restdocs.RestDocumentation`.
static `document` method on
`org.springframework.restdocs.mockmvc.MockMvcRestDocumentation`.
By default, three snippets a written:

View File

@@ -16,25 +16,32 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class AlwaysDo {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build");
private MockMvc mockMvc;
private WebApplicationContext context;
// tag::always-do[]
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document("{method-name}/{step}/"))
.build();
}

View File

@@ -1,3 +1,19 @@
/*
* Copyright 2014-2015 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;
import java.util.List;
@@ -15,17 +31,17 @@ public class Constraints {
ConstraintDescriptions userConstraints = new ConstraintDescriptions(UserInput.class); // <1>
List<String> descriptions = userConstraints.descriptionsForProperty("name"); // <2>
}
static class UserInput {
@NotNull
@Size(min = 1)
String name;
@NotNull
@Size(min = 8)
String password;
}
// end::constraints[]
}

View File

@@ -1,16 +1,37 @@
/*
* Copyright 2014-2015 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;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.curl.CurlDocumentation.curlRequest;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class CustomDefaultSnippetsConfiguration {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build");
@Autowired
private WebApplicationContext context;
@@ -20,7 +41,7 @@ public class CustomDefaultSnippetsConfiguration {
public void setUp() {
// tag::custom-default-snippets[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration().snippets()
.apply(documentationConfiguration(this.restDocumentation).snippets()
.withDefaults(curlRequest()))
.build();
// end::custom-default-snippets[]

View File

@@ -16,15 +16,20 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class CustomEncoding {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build");
@Autowired
private WebApplicationContext context;
@@ -35,7 +40,7 @@ public class CustomEncoding {
public void setUp() {
// tag::custom-encoding[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration().snippets()
.apply(documentationConfiguration(this.restDocumentation).snippets()
.withEncoding("ISO-8859-1"))
.build();
// end::custom-encoding[]

View File

@@ -16,15 +16,20 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class CustomUriConfiguration {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build");
@Autowired
private WebApplicationContext context;
@@ -35,7 +40,7 @@ public class CustomUriConfiguration {
public void setUp() {
// tag::custom-uri-configuration[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration().uris()
.apply(documentationConfiguration(this.restDocumentation).uris()
.withScheme("https")
.withHost("example.com")
.withPort(443))

View File

@@ -17,16 +17,21 @@
package com.example;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.restdocs.RestDocumentation;
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.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
public class ExampleApplicationTests {
// tag::mock-mvc-setup[]
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");
@Autowired
private WebApplicationContext context;
@@ -35,7 +40,7 @@ public class ExampleApplicationTests {
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
// end::mock-mvc-setup[]

View File

@@ -16,12 +16,12 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.halLinks;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

View File

@@ -16,8 +16,8 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.http.MediaType;

View File

@@ -16,8 +16,8 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

View File

@@ -16,9 +16,9 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;

View File

@@ -16,13 +16,13 @@
package com.example;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
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.removeHeaders;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.RestDocumentation.document;
import org.springframework.test.web.servlet.MockMvc;

View File

@@ -16,9 +16,9 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;