Skip to content

Commit 42353a0

Browse files
committed
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
1 parent cb1f98c commit 42353a0

File tree

9 files changed

+186
-24
lines changed

9 files changed

+186
-24
lines changed

docs/src/docs/asciidoc/customizing-requests-and-responses.adoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,17 @@ Then, in each test, any configuration specific to that test can be performed. Fo
5858
----
5959
include::{examples-dir}/com/example/mockmvc/EveryTestPreprocessing.java[tags=use]
6060
----
61-
<1> Document the links specific to the resource that is being tested
62-
<2> The request and response will be preprocessed due to the use of `alwaysDo` above.
61+
<1> The request and response will be preprocessed due to the use of `alwaysDo` above.
62+
<2> Document the links specific to the resource that is being tested
6363

6464
[source,java,indent=0,role="secondary"]
6565
.REST Assured
6666
----
6767
include::{examples-dir}/com/example/restassured/EveryTestPreprocessing.java[tags=use]
6868
----
69-
<1> Document the links specific to the resource that is being tested
70-
<2> The request and response will be preprocessed due to the configuration of the
69+
<1> The request and response will be preprocessed due to the configuration of the
7170
`RequestSpecification` in the `setUp` method.
71+
<2> Document the links specific to the resource that is being tested
7272

7373
Various built in preprocessors, including those illustrated above, are available via the
7474
static methods on `Preprocessors`. See <<Preprocessors, below>> for further details.

docs/src/test/java/com/example/mockmvc/EveryTestPreprocessing.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,27 +46,28 @@ public class EveryTestPreprocessing {
4646
// tag::setup[]
4747
private MockMvc mockMvc;
4848

49-
private RestDocumentationResultHandler document;
49+
private RestDocumentationResultHandler documentationHandler;
5050

5151
@Before
5252
public void setup() {
53-
this.document = document("{method-name}", // <1>
53+
this.documentationHandler = document("{method-name}", // <1>
5454
preprocessRequest(removeHeaders("Foo")),
5555
preprocessResponse(prettyPrint()));
5656
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
5757
.apply(documentationConfiguration(this.restDocumentation))
58-
.alwaysDo(this.document) // <2>
58+
.alwaysDo(this.documentationHandler) // <2>
5959
.build();
6060
}
6161

6262
// end::setup[]
6363

6464
public void use() throws Exception {
6565
// tag::use[]
66-
this.document.snippets( // <1>
67-
links(linkWithRel("self").description("Canonical self link")));
68-
this.mockMvc.perform(get("/")) // <2>
69-
.andExpect(status().isOk());
66+
this.mockMvc.perform(get("/")) // <1>
67+
.andExpect(status().isOk())
68+
.andDo(this.documentationHandler.document( // <2>
69+
links(linkWithRel("self").description("Canonical self link"))
70+
));
7071
// end::use[]
7172
}
7273

docs/src/test/java/com/example/restassured/EveryTestPreprocessing.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,26 +44,26 @@ public class EveryTestPreprocessing {
4444
// tag::setup[]
4545
private RequestSpecification spec;
4646

47-
private RestDocumentationFilter document;
47+
private RestDocumentationFilter documentationFilter;
4848

4949
@Before
5050
public void setup() {
51-
this.document = document("{method-name}",
51+
this.documentationFilter = document("{method-name}",
5252
preprocessRequest(removeHeaders("Foo")),
5353
preprocessResponse(prettyPrint())); // <1>
5454
this.spec = new RequestSpecBuilder()
5555
.addFilter(documentationConfiguration(this.restDocumentation))
56-
.addFilter(this.document)// <2>
56+
.addFilter(this.documentationFilter)// <2>
5757
.build();
5858
}
5959

6060
// end::setup[]
6161

6262
public void use() throws Exception {
6363
// tag::use[]
64-
this.document.snippets( // <1>
65-
links(linkWithRel("self").description("Canonical self link")));
66-
RestAssured.given(this.spec) // <2>
64+
RestAssured.given(this.spec) // <1>
65+
.filter(this.documentationFilter.document( // <2>
66+
links(linkWithRel("self").description("Canonical self link"))))
6767
.when().get("/")
6868
.then().assertThat().statusCode(is(200));
6969
// end::use[]

spring-restdocs-core/src/main/java/org/springframework/restdocs/generate/RestDocumentationGenerator.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @param <REQ> the request type that can be handled
4242
* @param <RESP> the response type that can be handled
4343
* @author Andy Wilkinson
44+
* @since 1.1
4445
*/
4546
public final class RestDocumentationGenerator<REQ, RESP> {
4647

@@ -205,11 +206,27 @@ public void handle(REQ request, RESP response, Map<String, Object> configuration
205206
* called.
206207
*
207208
* @param snippets the snippets to add
209+
* @deprecated since 1.1 in favor of {@link #withSnippets(Snippet...)}
208210
*/
211+
@Deprecated
209212
public void addSnippets(Snippet... snippets) {
210213
this.additionalSnippets.addAll(Arrays.asList(snippets));
211214
}
212215

216+
/**
217+
* Creates a new {@link RestDocumentationGenerator} with the same configuration as
218+
* this one other than its snippets. The new generator will use the given
219+
* {@code snippets}.
220+
*
221+
* @param snippets the snippets
222+
* @return the new generator
223+
*/
224+
public RestDocumentationGenerator<REQ, RESP> withSnippets(Snippet... snippets) {
225+
return new RestDocumentationGenerator<>(this.identifier, this.requestConverter,
226+
this.responseConverter, this.requestPreprocessor,
227+
this.responsePreprocessor, snippets);
228+
}
229+
213230
@SuppressWarnings("unchecked")
214231
private List<Snippet> getSnippets(Map<String, Object> configuration) {
215232
List<Snippet> combinedSnippets = new ArrayList<>(this.snippets);

spring-restdocs-core/src/test/java/org/springframework/restdocs/RestDocumentationGeneratorTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.springframework.restdocs.operation.OperationResponseFactory;
3636
import org.springframework.restdocs.operation.RequestConverter;
3737
import org.springframework.restdocs.operation.ResponseConverter;
38+
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
39+
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
3840
import org.springframework.restdocs.snippet.Snippet;
3941

4042
import static org.hamcrest.CoreMatchers.equalTo;
@@ -43,6 +45,7 @@
4345
import static org.mockito.BDDMockito.given;
4446
import static org.mockito.Mockito.mock;
4547
import static org.mockito.Mockito.verify;
48+
import static org.mockito.Mockito.verifyNoMoreInteractions;
4649

4750
/**
4851
* Tests for {@link RestDocumentationGenerator}.
@@ -105,6 +108,7 @@ public void defaultSnippetsAreCalled() throws IOException {
105108
}
106109

107110
@Test
111+
@Deprecated
108112
public void additionalSnippetsAreCalled() throws IOException {
109113
given(this.requestConverter.convert(this.request))
110114
.willReturn(this.operationRequest);
@@ -123,6 +127,33 @@ public void additionalSnippetsAreCalled() throws IOException {
123127
verifySnippetInvocation(additionalSnippet2, configuration);
124128
}
125129

130+
@Test
131+
public void newGeneratorOnlyCallsItsSnippets() throws IOException {
132+
OperationRequestPreprocessor requestPreprocessor = mock(
133+
OperationRequestPreprocessor.class);
134+
OperationResponsePreprocessor responsePreprocessor = mock(
135+
OperationResponsePreprocessor.class);
136+
given(this.requestConverter.convert(this.request))
137+
.willReturn(this.operationRequest);
138+
given(this.responseConverter.convert(this.response))
139+
.willReturn(this.operationResponse);
140+
given(requestPreprocessor.preprocess(this.operationRequest))
141+
.willReturn(this.operationRequest);
142+
given(responsePreprocessor.preprocess(this.operationResponse))
143+
.willReturn(this.operationResponse);
144+
Snippet additionalSnippet1 = mock(Snippet.class);
145+
Snippet additionalSnippet2 = mock(Snippet.class);
146+
RestDocumentationGenerator<Object, Object> generator = new RestDocumentationGenerator<>(
147+
"id", this.requestConverter, this.responseConverter, requestPreprocessor,
148+
responsePreprocessor, this.snippet);
149+
HashMap<String, Object> configuration = new HashMap<>();
150+
generator.withSnippets(additionalSnippet1, additionalSnippet2)
151+
.handle(this.request, this.response, configuration);
152+
verifyNoMoreInteractions(this.snippet);
153+
verifySnippetInvocation(additionalSnippet1, configuration);
154+
verifySnippetInvocation(additionalSnippet2, configuration);
155+
}
156+
126157
private void verifySnippetInvocation(Snippet snippet, Map<String, Object> attributes)
127158
throws IOException {
128159
verifySnippetInvocation(snippet, attributes, 1);

spring-restdocs-mockmvc/src/main/java/org/springframework/restdocs/mockmvc/RestDocumentationResultHandler.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.restdocs.mockmvc;
1818

19+
import java.util.HashMap;
1920
import java.util.Map;
2021

2122
import org.springframework.mock.web.MockHttpServletRequest;
@@ -59,10 +60,44 @@ public void handle(MvcResult result) throws Exception {
5960
*
6061
* @param snippets the snippets to add
6162
* @return this {@code RestDocumentationResultHandler}
63+
* @deprecated since 1.1 in favor of {@link #document(Snippet...)}
6264
*/
65+
@Deprecated
6366
public RestDocumentationResultHandler snippets(Snippet... snippets) {
6467
this.delegate.addSnippets(snippets);
6568
return this;
6669
}
6770

71+
/**
72+
* Creates a new {@link RestDocumentationResultHandler} that will produce
73+
* documentation using the given {@code snippets}.
74+
*
75+
* @param snippets the snippets
76+
* @return the new result handler
77+
*/
78+
public RestDocumentationResultHandler document(Snippet... snippets) {
79+
return new RestDocumentationResultHandler(this.delegate.withSnippets(snippets)) {
80+
81+
@Override
82+
public void handle(MvcResult result) throws Exception {
83+
@SuppressWarnings("unchecked")
84+
Map<String, Object> configuration = new HashMap<>(
85+
(Map<String, Object>) result.getRequest()
86+
.getAttribute(ATTRIBUTE_NAME_CONFIGURATION));
87+
configuration.remove(
88+
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS);
89+
getDelegate().handle(result.getRequest(), result.getResponse(),
90+
configuration);
91+
}
92+
};
93+
}
94+
95+
/**
96+
* Returns the {@link RestDocumentationGenerator} that is used as a delegate.
97+
*
98+
* @return the delegate
99+
*/
100+
protected final RestDocumentationGenerator<MockHttpServletRequest, MockHttpServletResponse> getDelegate() {
101+
return this.delegate;
102+
}
68103
}

spring-restdocs-mockmvc/src/test/java/org/springframework/restdocs/mockmvc/MockMvcRestDocumentationIntegrationTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
import static org.junit.Assert.assertThat;
5757
import static org.junit.Assert.assertTrue;
5858
import static org.springframework.restdocs.cli.CliDocumentation.curlRequest;
59+
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
60+
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
5961
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
6062
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
6163
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
@@ -329,6 +331,24 @@ public void multiStep() throws Exception {
329331
"http-response.adoc", "curl-request.adoc");
330332
}
331333

334+
@Test
335+
public void alwaysDoWithAdditionalSnippets() throws Exception {
336+
RestDocumentationResultHandler documentation = document("{method-name}-{step}");
337+
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
338+
.apply(documentationConfiguration(this.restDocumentation))
339+
.alwaysDo(documentation).build();
340+
341+
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
342+
.andExpect(status().isOk()).andDo(documentation.document(
343+
responseHeaders(headerWithName("a").description("one"))));
344+
345+
assertExpectedSnippetFilesExist(
346+
new File(
347+
"build/generated-snippets/always-do-with-additional-snippets-1/"),
348+
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
349+
"response-headers.adoc");
350+
}
351+
332352
@Test
333353
public void preprocessedRequest() throws Exception {
334354
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)

spring-restdocs-restassured/src/main/java/org/springframework/restdocs/restassured/RestDocumentationFilter.java

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
*
3636
* @author Andy Wilkinson
3737
*/
38-
public final class RestDocumentationFilter implements Filter {
38+
public class RestDocumentationFilter implements Filter {
3939

4040
static final String CONTEXT_KEY_CONFIGURATION = "org.springframework.restdocs.configuration";
4141

@@ -48,21 +48,35 @@ public final class RestDocumentationFilter implements Filter {
4848
}
4949

5050
@Override
51-
public Response filter(FilterableRequestSpecification requestSpec,
51+
public final Response filter(FilterableRequestSpecification requestSpec,
5252
FilterableResponseSpecification responseSpec, FilterContext context) {
5353
Response response = context.next(requestSpec, responseSpec);
5454

55+
Map<String, Object> configuration = getConfiguration(requestSpec, context);
56+
57+
this.delegate.handle(requestSpec, response, configuration);
58+
59+
return response;
60+
}
61+
62+
/**
63+
* Returns the configuration that should be used when calling the delgate. The
64+
* configuration is derived from the given {@code requestSpec} and {@code context}.
65+
*
66+
* @param requestSpec the request specification
67+
* @param context the filter context
68+
* @return the configuration
69+
*/
70+
protected Map<String, Object> getConfiguration(
71+
FilterableRequestSpecification requestSpec, FilterContext context) {
5572
Map<String, Object> configuration = new HashMap<>(
5673
context.<Map<String, Object>>getValue(CONTEXT_KEY_CONFIGURATION));
5774
configuration.put(RestDocumentationContext.class.getName(),
5875
context.<RestDocumentationContext>getValue(
5976
RestDocumentationContext.class.getName()));
6077
configuration.put(RestDocumentationGenerator.ATTRIBUTE_NAME_URL_TEMPLATE,
6178
requestSpec.getUserDefinedPath());
62-
63-
this.delegate.handle(requestSpec, response, configuration);
64-
65-
return response;
79+
return configuration;
6680
}
6781

6882
/**
@@ -71,10 +85,35 @@ public Response filter(FilterableRequestSpecification requestSpec,
7185
*
7286
* @param snippets the snippets to add
7387
* @return this {@code RestDocumentationFilter}
88+
* @deprecated since 1.1 in favor of {@link #document(Snippet...)}
7489
*/
75-
public RestDocumentationFilter snippets(Snippet... snippets) {
90+
@Deprecated
91+
public final RestDocumentationFilter snippets(Snippet... snippets) {
7692
this.delegate.addSnippets(snippets);
7793
return this;
7894
}
7995

96+
/**
97+
* Creates a new {@link RestDocumentationFilter} that will produce documentation using
98+
* the given {@code snippets}.
99+
*
100+
* @param snippets the snippets
101+
* @return the new result handler
102+
*/
103+
public final RestDocumentationFilter document(Snippet... snippets) {
104+
return new RestDocumentationFilter(this.delegate.withSnippets(snippets)) {
105+
106+
@Override
107+
protected Map<String, Object> getConfiguration(
108+
FilterableRequestSpecification requestSpec, FilterContext context) {
109+
Map<String, Object> configuration = super.getConfiguration(requestSpec,
110+
context);
111+
configuration.remove(
112+
RestDocumentationGenerator.ATTRIBUTE_NAME_DEFAULT_SNIPPETS);
113+
return configuration;
114+
}
115+
116+
};
117+
}
118+
80119
}

spring-restdocs-restassured/src/test/java/org/springframework/restdocs/restassured/RestAssuredRestDocumentationIntegrationTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import static org.hamcrest.CoreMatchers.is;
5353
import static org.junit.Assert.assertThat;
5454
import static org.junit.Assert.assertTrue;
55+
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
56+
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
5557
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
5658
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
5759
import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks;
@@ -240,6 +242,23 @@ public void multiStep() throws Exception {
240242
"http-response.adoc", "curl-request.adoc");
241243
}
242244

245+
@Test
246+
public void additionalSnippets() throws Exception {
247+
RestDocumentationFilter documentation = document("{method-name}-{step}");
248+
RequestSpecification spec = new RequestSpecBuilder().setPort(this.port)
249+
.addFilter(documentationConfiguration(this.restDocumentation))
250+
.addFilter(documentation).build();
251+
given(spec)
252+
.filter(documentation
253+
.document(responseHeaders(headerWithName("a").description("one"),
254+
headerWithName("Foo").description("two"))))
255+
.get("/").then().statusCode(200);
256+
assertExpectedSnippetFilesExist(
257+
new File("build/generated-snippets/additional-snippets-1/"),
258+
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
259+
"response-headers.adoc");
260+
}
261+
243262
@Test
244263
public void preprocessedRequest() throws Exception {
245264
Pattern pattern = Pattern.compile("(\"alpha\")");

0 commit comments

Comments
 (0)