Improve the API for generating additional snippets
Previously, if alwaysDo was used and the user wanted to generate one or more additional snippets when calling perform a separate call to `snippets` was made prior to calling `perform`. This had two problems: - it required the result handler to be stateful (see gh-243) - it wasn't clear that the additional snippets would be produced when a subsequent call to perform was made This commit introduces a new API that allows the additional snippets to be specified within the MockMvc call. The old API has been deprecated and will be removed in 2.0. Closes gh-249
This commit is contained in:
@@ -58,17 +58,17 @@ Then, in each test, any configuration specific to that test can be performed. Fo
|
||||
----
|
||||
include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=use]
|
||||
----
|
||||
<1> Document the links specific to the resource that is being tested
|
||||
<2> The request and response will be preprocessed due to the use of `alwaysDo` above.
|
||||
<1> The request and response will be preprocessed due to the use of `alwaysDo` above.
|
||||
<2> Document the links specific to the resource that is being tested
|
||||
|
||||
[source,java,indent=0,role="secondary"]
|
||||
.REST Assured
|
||||
----
|
||||
include::{examples-dir}/com/example/restassured/EveryTestPreprocessing.java[tags=use]
|
||||
----
|
||||
<1> Document the links specific to the resource that is being tested
|
||||
<2> The request and response will be preprocessed due to the configuration of the
|
||||
<1> The request and response will be preprocessed due to the configuration of the
|
||||
`RequestSpecification` in the `setUp` method.
|
||||
<2> Document the links specific to the resource that is being tested
|
||||
|
||||
Various built in preprocessors, including those illustrated above, are available via the
|
||||
static methods on `Preprocessors`. See <<Preprocessors, below>> for further details.
|
||||
|
||||
@@ -46,16 +46,16 @@ public class EveryTestPreprocessing {
|
||||
// tag::setup[]
|
||||
private MockMvc mockMvc;
|
||||
|
||||
private RestDocumentationResultHandler document;
|
||||
private RestDocumentationResultHandler documentationHandler;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.document = document("{method-name}", // <1>
|
||||
this.documentationHandler = document("{method-name}", // <1>
|
||||
preprocessRequest(removeHeaders("Foo")),
|
||||
preprocessResponse(prettyPrint()));
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(documentationConfiguration(this.restDocumentation))
|
||||
.alwaysDo(this.document) // <2>
|
||||
.alwaysDo(this.documentationHandler) // <2>
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -63,10 +63,11 @@ public class EveryTestPreprocessing {
|
||||
|
||||
public void use() throws Exception {
|
||||
// tag::use[]
|
||||
this.document.snippets( // <1>
|
||||
links(linkWithRel("self").description("Canonical self link")));
|
||||
this.mockMvc.perform(get("/")) // <2>
|
||||
.andExpect(status().isOk());
|
||||
this.mockMvc.perform(get("/")) // <1>
|
||||
.andExpect(status().isOk())
|
||||
.andDo(this.documentationHandler.document( // <2>
|
||||
links(linkWithRel("self").description("Canonical self link"))
|
||||
));
|
||||
// end::use[]
|
||||
}
|
||||
|
||||
|
||||
@@ -44,16 +44,16 @@ public class EveryTestPreprocessing {
|
||||
// tag::setup[]
|
||||
private RequestSpecification spec;
|
||||
|
||||
private RestDocumentationFilter document;
|
||||
private RestDocumentationFilter documentationFilter;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.document = document("{method-name}",
|
||||
this.documentationFilter = document("{method-name}",
|
||||
preprocessRequest(removeHeaders("Foo")),
|
||||
preprocessResponse(prettyPrint())); // <1>
|
||||
this.spec = new RequestSpecBuilder()
|
||||
.addFilter(documentationConfiguration(this.restDocumentation))
|
||||
.addFilter(this.document)// <2>
|
||||
.addFilter(this.documentationFilter)// <2>
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ public class EveryTestPreprocessing {
|
||||
|
||||
public void use() throws Exception {
|
||||
// tag::use[]
|
||||
this.document.snippets( // <1>
|
||||
links(linkWithRel("self").description("Canonical self link")));
|
||||
RestAssured.given(this.spec) // <2>
|
||||
RestAssured.given(this.spec) // <1>
|
||||
.filter(this.documentationFilter.document( // <2>
|
||||
links(linkWithRel("self").description("Canonical self link"))))
|
||||
.when().get("/")
|
||||
.then().assertThat().statusCode(is(200));
|
||||
// end::use[]
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.util.Assert;
|
||||
* @param <REQ> the request type that can be handled
|
||||
* @param <RESP> the response type that can be handled
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1
|
||||
*/
|
||||
public final class RestDocumentationGenerator<REQ, RESP> {
|
||||
|
||||
@@ -205,11 +206,27 @@ public final class RestDocumentationGenerator<REQ, RESP> {
|
||||
* called.
|
||||
*
|
||||
* @param snippets the snippets to add
|
||||
* @deprecated since 1.1 in favor of {@link #withSnippets(Snippet...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addSnippets(Snippet... snippets) {
|
||||
this.additionalSnippets.addAll(Arrays.asList(snippets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RestDocumentationGenerator} with the same configuration as
|
||||
* this one other than its snippets. The new generator will use the given
|
||||
* {@code snippets}.
|
||||
*
|
||||
* @param snippets the snippets
|
||||
* @return the new generator
|
||||
*/
|
||||
public RestDocumentationGenerator<REQ, RESP> withSnippets(Snippet... snippets) {
|
||||
return new RestDocumentationGenerator<>(this.identifier, this.requestConverter,
|
||||
this.responseConverter, this.requestPreprocessor,
|
||||
this.responsePreprocessor, snippets);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<Snippet> getSnippets(Map<String, Object> configuration) {
|
||||
List<Snippet> combinedSnippets = new ArrayList<>(this.snippets);
|
||||
|
||||
@@ -35,6 +35,8 @@ import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.RequestConverter;
|
||||
import org.springframework.restdocs.operation.ResponseConverter;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
|
||||
import org.springframework.restdocs.snippet.Snippet;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
@@ -43,6 +45,7 @@ import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link RestDocumentationGenerator}.
|
||||
@@ -105,6 +108,7 @@ public class RestDocumentationGeneratorTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Deprecated
|
||||
public void additionalSnippetsAreCalled() throws IOException {
|
||||
given(this.requestConverter.convert(this.request))
|
||||
.willReturn(this.operationRequest);
|
||||
@@ -123,6 +127,33 @@ public class RestDocumentationGeneratorTests {
|
||||
verifySnippetInvocation(additionalSnippet2, configuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newGeneratorOnlyCallsItsSnippets() throws IOException {
|
||||
OperationRequestPreprocessor requestPreprocessor = mock(
|
||||
OperationRequestPreprocessor.class);
|
||||
OperationResponsePreprocessor responsePreprocessor = mock(
|
||||
OperationResponsePreprocessor.class);
|
||||
given(this.requestConverter.convert(this.request))
|
||||
.willReturn(this.operationRequest);
|
||||
given(this.responseConverter.convert(this.response))
|
||||
.willReturn(this.operationResponse);
|
||||
given(requestPreprocessor.preprocess(this.operationRequest))
|
||||
.willReturn(this.operationRequest);
|
||||
given(responsePreprocessor.preprocess(this.operationResponse))
|
||||
.willReturn(this.operationResponse);
|
||||
Snippet additionalSnippet1 = mock(Snippet.class);
|
||||
Snippet additionalSnippet2 = mock(Snippet.class);
|
||||
RestDocumentationGenerator<Object, Object> generator = new RestDocumentationGenerator<>(
|
||||
"id", this.requestConverter, this.responseConverter, requestPreprocessor,
|
||||
responsePreprocessor, this.snippet);
|
||||
HashMap<String, Object> configuration = new HashMap<>();
|
||||
generator.withSnippets(additionalSnippet1, additionalSnippet2)
|
||||
.handle(this.request, this.response, configuration);
|
||||
verifyNoMoreInteractions(this.snippet);
|
||||
verifySnippetInvocation(additionalSnippet1, configuration);
|
||||
verifySnippetInvocation(additionalSnippet2, configuration);
|
||||
}
|
||||
|
||||
private void verifySnippetInvocation(Snippet snippet, Map<String, Object> attributes)
|
||||
throws IOException {
|
||||
verifySnippetInvocation(snippet, attributes, 1);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.restdocs.mockmvc;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -59,10 +60,44 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
*
|
||||
* @param snippets the snippets to add
|
||||
* @return this {@code RestDocumentationResultHandler}
|
||||
* @deprecated since 1.1 in favor of {@link #document(Snippet...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public RestDocumentationResultHandler snippets(Snippet... snippets) {
|
||||
this.delegate.addSnippets(snippets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RestDocumentationResultHandler} that will produce
|
||||
* documentation using the given {@code snippets}.
|
||||
*
|
||||
* @param snippets the snippets
|
||||
* @return the new result handler
|
||||
*/
|
||||
public RestDocumentationResultHandler document(Snippet... snippets) {
|
||||
return new RestDocumentationResultHandler(this.delegate.withSnippets(snippets)) {
|
||||
|
||||
@Override
|
||||
public void handle(MvcResult result) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> configuration = new HashMap<>(
|
||||
(Map<String, Object>) result.getRequest()
|
||||
.getAttribute(ATTRIBUTE_NAME_CONFIGURATION));
|
||||
configuration.remove(
|
||||
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS);
|
||||
getDelegate().handle(result.getRequest(), result.getResponse(),
|
||||
configuration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link RestDocumentationGenerator} that is used as a delegate.
|
||||
*
|
||||
* @return the delegate
|
||||
*/
|
||||
protected final RestDocumentationGenerator<MockHttpServletRequest, MockHttpServletResponse> getDelegate() {
|
||||
return this.delegate;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.restdocs.cli.CliDocumentation.curlRequest;
|
||||
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
|
||||
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
@@ -329,6 +331,24 @@ public class MockMvcRestDocumentationIntegrationTests {
|
||||
"http-response.adoc", "curl-request.adoc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alwaysDoWithAdditionalSnippets() throws Exception {
|
||||
RestDocumentationResultHandler documentation = document("{method-name}-{step}");
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(documentationConfiguration(this.restDocumentation))
|
||||
.alwaysDo(documentation).build();
|
||||
|
||||
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
|
||||
.andExpect(status().isOk()).andDo(documentation.document(
|
||||
responseHeaders(headerWithName("a").description("one"))));
|
||||
|
||||
assertExpectedSnippetFilesExist(
|
||||
new File(
|
||||
"build/generated-snippets/always-do-with-additional-snippets-1/"),
|
||||
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
|
||||
"response-headers.adoc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preprocessedRequest() throws Exception {
|
||||
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class RestDocumentationFilter implements Filter {
|
||||
public class RestDocumentationFilter implements Filter {
|
||||
|
||||
static final String CONTEXT_KEY_CONFIGURATION = "org.springframework.restdocs.configuration";
|
||||
|
||||
@@ -48,10 +48,27 @@ public final class RestDocumentationFilter implements Filter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response filter(FilterableRequestSpecification requestSpec,
|
||||
public final Response filter(FilterableRequestSpecification requestSpec,
|
||||
FilterableResponseSpecification responseSpec, FilterContext context) {
|
||||
Response response = context.next(requestSpec, responseSpec);
|
||||
|
||||
Map<String, Object> configuration = getConfiguration(requestSpec, context);
|
||||
|
||||
this.delegate.handle(requestSpec, response, configuration);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration that should be used when calling the delgate. The
|
||||
* configuration is derived from the given {@code requestSpec} and {@code context}.
|
||||
*
|
||||
* @param requestSpec the request specification
|
||||
* @param context the filter context
|
||||
* @return the configuration
|
||||
*/
|
||||
protected Map<String, Object> getConfiguration(
|
||||
FilterableRequestSpecification requestSpec, FilterContext context) {
|
||||
Map<String, Object> configuration = new HashMap<>(
|
||||
context.<Map<String, Object>>getValue(CONTEXT_KEY_CONFIGURATION));
|
||||
configuration.put(RestDocumentationContext.class.getName(),
|
||||
@@ -59,10 +76,7 @@ public final class RestDocumentationFilter implements Filter {
|
||||
RestDocumentationContext.class.getName()));
|
||||
configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE,
|
||||
requestSpec.getUserDefinedPath());
|
||||
|
||||
this.delegate.handle(requestSpec, response, configuration);
|
||||
|
||||
return response;
|
||||
return configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,10 +85,35 @@ public final class RestDocumentationFilter implements Filter {
|
||||
*
|
||||
* @param snippets the snippets to add
|
||||
* @return this {@code RestDocumentationFilter}
|
||||
* @deprecated since 1.1 in favor of {@link #document(Snippet...)}
|
||||
*/
|
||||
public RestDocumentationFilter snippets(Snippet... snippets) {
|
||||
@Deprecated
|
||||
public final RestDocumentationFilter snippets(Snippet... snippets) {
|
||||
this.delegate.addSnippets(snippets);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link RestDocumentationFilter} that will produce documentation using
|
||||
* the given {@code snippets}.
|
||||
*
|
||||
* @param snippets the snippets
|
||||
* @return the new result handler
|
||||
*/
|
||||
public final RestDocumentationFilter document(Snippet... snippets) {
|
||||
return new RestDocumentationFilter(this.delegate.withSnippets(snippets)) {
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> getConfiguration(
|
||||
FilterableRequestSpecification requestSpec, FilterContext context) {
|
||||
Map<String, Object> configuration = super.getConfiguration(requestSpec,
|
||||
context);
|
||||
configuration.remove(
|
||||
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS);
|
||||
return configuration;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,6 +52,8 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
|
||||
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
||||
import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks;
|
||||
@@ -240,6 +242,23 @@ public class RestAssuredRestDocumentationIntegrationTests {
|
||||
"http-response.adoc", "curl-request.adoc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalSnippets() throws Exception {
|
||||
RestDocumentationFilter documentation = document("{method-name}-{step}");
|
||||
RequestSpecification spec = new RequestSpecBuilder().setPort(this.port)
|
||||
.addFilter(documentationConfiguration(this.restDocumentation))
|
||||
.addFilter(documentation).build();
|
||||
given(spec)
|
||||
.filter(documentation
|
||||
.document(responseHeaders(headerWithName("a").description("one"),
|
||||
headerWithName("Foo").description("two"))))
|
||||
.get("/").then().statusCode(200);
|
||||
assertExpectedSnippetFilesExist(
|
||||
new File("build/generated-snippets/additional-snippets-1/"),
|
||||
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
|
||||
"response-headers.adoc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preprocessedRequest() throws Exception {
|
||||
Pattern pattern = Pattern.compile("(\"alpha\")");
|
||||
|
||||
Reference in New Issue
Block a user