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

@@ -0,0 +1,35 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* Abstract configurer that declares methods that are internal to the documentation
* configuration implementation.
*
* @author Andy Wilkinson
*/
abstract class AbstractConfigurer {
/**
* Applies the configuration, possibly by modifying the given {@code request}
* @param request the request that may be modified
*/
abstract void apply(MockHttpServletRequest request);
}

View File

@@ -0,0 +1,55 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.context.WebApplicationContext;
/**
* Base class for {@link NestedConfigurer} implementations.
*
* @author Andy Wilkinson
* @param <PARENT> The type of the configurer's parent
*/
abstract class AbstractNestedConfigurer<PARENT extends MockMvcConfigurer> extends
AbstractConfigurer implements NestedConfigurer<PARENT>, MockMvcConfigurer {
private final PARENT parent;
protected AbstractNestedConfigurer(PARENT parent) {
this.parent = parent;
}
@Override
public PARENT and() {
return this.parent;
}
@Override
public void afterConfigurerAdded(ConfigurableMockMvcBuilder<?> builder) {
this.parent.afterConfigurerAdded(builder);
}
@Override
public RequestPostProcessor beforeMockMvcCreated(
ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
return this.parent.beforeMockMvcCreated(builder, context);
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import static org.springframework.restdocs.mockmvc.util.IterableEnumeration.iterable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.Part;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.StandardOperationRequest;
import org.springframework.restdocs.operation.StandardOperationRequestPart;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
/**
* A factory for creating an {@link OperationRequest} from a
* {@link MockHttpServletRequest}.
*
* @author Andy Wilkinson
*
*/
class MockMvcOperationRequestFactory {
private static final String SCHEME_HTTP = "http";
private static final String SCHEME_HTTPS = "https";
private static final int STANDARD_PORT_HTTP = 80;
private static final int STANDARD_PORT_HTTPS = 443;
/**
* Creates a new {@code OperationRequest} derived from the given {@code mockRequest}.
*
* @param mockRequest the request
* @return the {@code OperationRequest}
* @throws Exception if the request could not be created
*/
public OperationRequest createOperationRequest(MockHttpServletRequest mockRequest)
throws Exception {
HttpHeaders headers = extractHeaders(mockRequest);
Parameters parameters = extractParameters(mockRequest);
List<OperationRequestPart> parts = extractParts(mockRequest);
String queryString = mockRequest.getQueryString();
if (!StringUtils.hasText(queryString) && "GET".equals(mockRequest.getMethod())) {
queryString = parameters.toQueryString();
}
return new StandardOperationRequest(URI.create(getRequestUri(mockRequest)
+ (StringUtils.hasText(queryString) ? "?" + queryString : "")),
HttpMethod.valueOf(mockRequest.getMethod()),
FileCopyUtils.copyToByteArray(mockRequest.getInputStream()), headers,
parameters, parts);
}
private List<OperationRequestPart> extractParts(MockHttpServletRequest servletRequest)
throws IOException, ServletException {
List<OperationRequestPart> parts = new ArrayList<>();
for (Part part : servletRequest.getParts()) {
HttpHeaders partHeaders = extractHeaders(part);
List<String> contentTypeHeader = partHeaders.get(HttpHeaders.CONTENT_TYPE);
if (part.getContentType() != null && contentTypeHeader == null) {
partHeaders
.setContentType(MediaType.parseMediaType(part.getContentType()));
}
parts.add(new StandardOperationRequestPart(part.getName(), StringUtils
.hasText(part.getSubmittedFileName()) ? part.getSubmittedFileName()
: null, FileCopyUtils.copyToByteArray(part.getInputStream()),
partHeaders));
}
if (servletRequest instanceof MockMultipartHttpServletRequest) {
for (Entry<String, List<MultipartFile>> entry : ((MockMultipartHttpServletRequest) servletRequest)
.getMultiFileMap().entrySet()) {
for (MultipartFile file : entry.getValue()) {
HttpHeaders partHeaders = new HttpHeaders();
if (StringUtils.hasText(file.getContentType())) {
partHeaders.setContentType(MediaType.parseMediaType(file
.getContentType()));
}
parts.add(new StandardOperationRequestPart(file.getName(),
StringUtils.hasText(file.getOriginalFilename()) ? file
.getOriginalFilename() : null, file.getBytes(),
partHeaders));
}
}
}
return parts;
}
private HttpHeaders extractHeaders(Part part) {
HttpHeaders partHeaders = new HttpHeaders();
for (String headerName : part.getHeaderNames()) {
for (String value : part.getHeaders(headerName)) {
partHeaders.add(headerName, value);
}
}
return partHeaders;
}
private Parameters extractParameters(MockHttpServletRequest servletRequest) {
Parameters parameters = new Parameters();
for (String name : iterable(servletRequest.getParameterNames())) {
for (String value : servletRequest.getParameterValues(name)) {
parameters.add(name, value);
}
}
return parameters;
}
private HttpHeaders extractHeaders(MockHttpServletRequest servletRequest) {
HttpHeaders headers = new HttpHeaders();
for (String headerName : iterable(servletRequest.getHeaderNames())) {
for (String value : iterable(servletRequest.getHeaders(headerName))) {
headers.add(headerName, value);
}
}
return headers;
}
private boolean isNonStandardPort(MockHttpServletRequest request) {
return (SCHEME_HTTP.equals(request.getScheme()) && request.getServerPort() != STANDARD_PORT_HTTP)
|| (SCHEME_HTTPS.equals(request.getScheme()) && request.getServerPort() != STANDARD_PORT_HTTPS);
}
private String getRequestUri(MockHttpServletRequest request) {
StringWriter uriWriter = new StringWriter();
PrintWriter printer = new PrintWriter(uriWriter);
printer.printf("%s://%s", request.getScheme(), request.getServerName());
if (isNonStandardPort(request)) {
printer.printf(":%d", request.getServerPort());
}
printer.print(request.getRequestURI());
return uriWriter.toString();
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.restdocs.operation.StandardOperationResponse;
/**
* A factory for creating an {@link OperationResponse} derived from a
* {@link MockHttpServletResponse}.
*
* @author Andy Wilkinson
*/
class MockMvcOperationResponseFactory {
/**
* Create a new {@code OperationResponse} derived from the given {@code mockResponse}.
*
* @param mockResponse the response
* @return the {@code OperationResponse}
*/
public OperationResponse createOperationResponse(MockHttpServletResponse mockResponse) {
return new StandardOperationResponse(
HttpStatus.valueOf(mockResponse.getStatus()),
extractHeaders(mockResponse), mockResponse.getContentAsByteArray());
}
private HttpHeaders extractHeaders(MockHttpServletResponse response) {
HttpHeaders headers = new HttpHeaders();
for (String headerName : response.getHeaderNames()) {
for (String value : response.getHeaders(headerName)) {
headers.add(headerName, value);
}
}
return headers;
}
}

View File

@@ -0,0 +1,124 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
/**
* Static factory methods for documenting RESTful APIs using Spring MVC Test
*
* @author Andy Wilkinson
*/
public abstract class MockMvcRestDocumentation {
private MockMvcRestDocumentation() {
}
/**
* Provides access to a {@link MockMvcConfigurer} that can be used to configure a
* {@link MockMvc} instance using the given {@code restDocumentation}.
*
* @param restDocumentation the REST documentation
* @return the configurer
* @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer)
*/
public static RestDocumentationMockMvcConfigurer documentationConfiguration(
RestDocumentation restDocumentation) {
return new RestDocumentationMockMvcConfigurer(restDocumentation);
}
/**
* Documents the API call with the given {@code identifier} using the given
* {@code snippets}.
*
* @param identifier an identifier for the API call that is being documented
* @param snippets the snippets that will document the API call
* @return a Mock MVC {@code ResultHandler} that will produce the documentation
* @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder)
* @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler)
*/
public static RestDocumentationResultHandler document(String identifier,
Snippet... snippets) {
return new RestDocumentationResultHandler(identifier, snippets);
}
/**
* Documents the API call with the given {@code identifier} using the given
* {@code snippets}. The given {@code requestPreprocessor} is applied to the request
* before it is documented.
*
* @param identifier an identifier for the API call that is being documented
* @param requestPreprocessor the request preprocessor
* @param snippets the snippets that will document the API call
* @return a Mock MVC {@code ResultHandler} that will produce the documentation
* @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder)
* @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler)
*/
public static RestDocumentationResultHandler document(String identifier,
OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
return new RestDocumentationResultHandler(identifier, requestPreprocessor,
snippets);
}
/**
* Documents the API call with the given {@code identifier} using the given
* {@code snippets}. The given {@code responsePreprocessor} is applied to the request
* before it is documented.
*
* @param identifier an identifier for the API call that is being documented
* @param responsePreprocessor the response preprocessor
* @param snippets the snippets that will document the API call
* @return a Mock MVC {@code ResultHandler} that will produce the documentation
* @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder)
* @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler)
*/
public static RestDocumentationResultHandler document(String identifier,
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
return new RestDocumentationResultHandler(identifier, responsePreprocessor,
snippets);
}
/**
* Documents the API call with the given {@code identifier} using the given
* {@code snippets}. The given {@code requestPreprocessor} and
* {@code responsePreprocessor} are applied to the request and response respectively
* before they are documented.
*
* @param identifier an identifier for the API call that is being documented
* @param requestPreprocessor the request preprocessor
* @param responsePreprocessor the response preprocessor
* @param snippets the snippets that will document the API call
* @return a Mock MVC {@code ResultHandler} that will produce the documentation
* @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder)
* @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler)
*/
public static RestDocumentationResultHandler document(String identifier,
OperationRequestPreprocessor requestPreprocessor,
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
return new RestDocumentationResultHandler(identifier, requestPreprocessor,
responsePreprocessor, snippets);
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
/**
* A configurer that is nested and, therefore, has a parent.
*
* @author Andy Wilkinson
* @param <PARENT> The parent's type
*/
interface NestedConfigurer<PARENT extends MockMvcConfigurer> {
/**
* Returns the configurer's parent
*
* @return the parent
*/
PARENT and();
}

View File

@@ -0,0 +1,195 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
import org.springframework.restdocs.snippet.StandardWriterResolver;
import org.springframework.restdocs.snippet.WriterResolver;
import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
/**
* A {@link MockMvcConfigurer} that can be used to configure the documentation
*
* @author Andy Wilkinson
* @author Dmitriy Mayboroda
* @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer)
* @see MockMvcRestDocumentation#documentationConfiguration(RestDocumentation)
*/
public class RestDocumentationMockMvcConfigurer extends MockMvcConfigurerAdapter {
private final UriConfigurer uriConfigurer = new UriConfigurer(this);
private final SnippetConfigurer snippetConfigurer = new SnippetConfigurer(this);
private final RequestPostProcessor requestPostProcessor;
private final TemplateEngineConfigurer templateEngineConfigurer = new TemplateEngineConfigurer();
private final WriterResolverConfigurer writerResolverConfigurer = new WriterResolverConfigurer();
/**
* Creates a new {code RestDocumentationMockMvcConfigurer} that will use the given
* {@code restDocumentation} when configuring MockMvc.
*
* @param restDocumentation the rest documentation
* @see MockMvcRestDocumentation#documentationConfiguration(RestDocumentation)
*/
RestDocumentationMockMvcConfigurer(RestDocumentation restDocumentation) {
this.requestPostProcessor = new ConfigurerApplyingRequestPostProcessor(
restDocumentation, this.uriConfigurer, this.writerResolverConfigurer,
this.snippetConfigurer, new ContentLengthHeaderConfigurer(),
this.templateEngineConfigurer);
}
/**
* Returns a {@link UriConfigurer} that can be used to configure the request URIs that
* will be documented.
*
* @return the URI configurer
*/
public UriConfigurer uris() {
return this.uriConfigurer;
}
/**
* Returns a {@link SnippetConfigurer} that can be used to configure the snippets that
* will be generated.
*
* @return the snippet configurer
*/
public SnippetConfigurer snippets() {
return this.snippetConfigurer;
}
/**
* Configures the {@link TemplateEngine} that will be used for snippet rendering.
*
* @param templateEngine the template engine to use
* @return {@code this}
*/
public RestDocumentationMockMvcConfigurer templateEngine(TemplateEngine templateEngine) {
this.templateEngineConfigurer.setTemplateEngine(templateEngine);
return this;
}
/**
* Configures the {@link WriterResolver} that will be used to resolve a writer for a
* snippet.
*
* @param writerResolver The writer resolver to use
* @return {@code this}
*/
public RestDocumentationMockMvcConfigurer writerResolver(WriterResolver writerResolver) {
this.writerResolverConfigurer.setWriterResolver(writerResolver);
return this;
}
@Override
public RequestPostProcessor beforeMockMvcCreated(
ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
return this.requestPostProcessor;
}
private static class ContentLengthHeaderConfigurer extends AbstractConfigurer {
@Override
void apply(MockHttpServletRequest request) {
long contentLength = request.getContentLengthLong();
if (contentLength > 0
&& !StringUtils.hasText(request.getHeader("Content-Length"))) {
request.addHeader("Content-Length", request.getContentLengthLong());
}
}
}
private static class TemplateEngineConfigurer extends AbstractConfigurer {
private TemplateEngine templateEngine = new MustacheTemplateEngine(
new StandardTemplateResourceResolver());
@Override
void apply(MockHttpServletRequest request) {
request.setAttribute(TemplateEngine.class.getName(), this.templateEngine);
}
void setTemplateEngine(TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
}
}
private static class WriterResolverConfigurer extends AbstractConfigurer {
private WriterResolver writerResolver;
@Override
void apply(MockHttpServletRequest request) {
WriterResolver resolverToUse = this.writerResolver;
if (resolverToUse == null) {
resolverToUse = new StandardWriterResolver(
new RestDocumentationContextPlaceholderResolver(
(RestDocumentationContext) request
.getAttribute(RestDocumentationContext.class
.getName())));
}
request.setAttribute(WriterResolver.class.getName(), resolverToUse);
}
void setWriterResolver(WriterResolver writerResolver) {
this.writerResolver = writerResolver;
}
}
private static class ConfigurerApplyingRequestPostProcessor implements
RequestPostProcessor {
private final RestDocumentation restDocumentation;
private final AbstractConfigurer[] configurers;
private ConfigurerApplyingRequestPostProcessor(
RestDocumentation restDocumentation, AbstractConfigurer... configurers) {
this.restDocumentation = restDocumentation;
this.configurers = configurers;
}
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
request.setAttribute(RestDocumentationContext.class.getName(),
this.restDocumentation.beforeOperation());
for (AbstractConfigurer configurer : this.configurers) {
configurer.apply(request);
}
return request;
}
}
}

View File

@@ -0,0 +1,266 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import java.net.URI;
import org.springframework.http.HttpMethod;
import org.springframework.restdocs.request.RequestDocumentation;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
/**
* A drop-in replacement for {@link MockMvcRequestBuilders} that captures a request's URL
* template and makes it available for documentation. Required when
* {@link RequestDocumentation#pathParameters(org.springframework.restdocs.request.ParameterDescriptor...)
* ) documenting path parameters} and recommended for general usage.
*
* @author Andy Wilkinson
* @see MockMvcRequestBuilders
* @see RequestDocumentation#pathParameters(org.springframework.restdocs.request.ParameterDescriptor...)
* @see RequestDocumentation#pathParameters(java.util.Map,
* org.springframework.restdocs.request.ParameterDescriptor...)
*/
public abstract class RestDocumentationRequestBuilders {
private static final String ATTRIBUTE_NAME_URL_TEMPLATE = "org.springframework.restdocs.urlTemplate";
private RestDocumentationRequestBuilders() {
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the GET request
*/
public static MockHttpServletRequestBuilder get(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.get(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request.
*
* @param uri the URL
* @return the builder for the GET request
*/
public static MockHttpServletRequestBuilder get(URI uri) {
return MockMvcRequestBuilders.get(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the POST request
*/
public static MockHttpServletRequestBuilder post(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.post(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request.
*
* @param uri the URL
* @return the builder for the POST request
*/
public static MockHttpServletRequestBuilder post(URI uri) {
return MockMvcRequestBuilders.post(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the PUT request
*/
public static MockHttpServletRequestBuilder put(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.put(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request.
*
* @param uri the URL
* @return the builder for the PUT request
*/
public static MockHttpServletRequestBuilder put(URI uri) {
return MockMvcRequestBuilders.put(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the PATCH request
*/
public static MockHttpServletRequestBuilder patch(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.patch(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request.
*
* @param uri the URL
* @return the builder for the PATCH request
*/
public static MockHttpServletRequestBuilder patch(URI uri) {
return MockMvcRequestBuilders.patch(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the DELETE request
*/
public static MockHttpServletRequestBuilder delete(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.delete(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request.
*
* @param uri the URL
* @return the builder for the DELETE request
*/
public static MockHttpServletRequestBuilder delete(URI uri) {
return MockMvcRequestBuilders.delete(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the OPTIONS request
*/
public static MockHttpServletRequestBuilder options(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.options(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request.
*
* @param uri the URL
* @return the builder for the OPTIONS request
*/
public static MockHttpServletRequestBuilder options(URI uri) {
return MockMvcRequestBuilders.options(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a HEAD request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the HEAD request
*/
public static MockHttpServletRequestBuilder head(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.head(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a HEAD request.
*
* @param uri the URL
* @return the builder for the HEAD request
*/
public static MockHttpServletRequestBuilder head(URI uri) {
return MockMvcRequestBuilders.head(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP
* method. The url template will be captured and made available for documentation.
*
* @param httpMethod the HTTP method
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the request
*/
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod,
String urlTemplate, Object... urlVariables) {
return MockMvcRequestBuilders.request(httpMethod, urlTemplate, urlVariables)
.requestAttr(ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP
* method.
* @param httpMethod the HTTP method (GET, POST, etc)
* @param uri the URL
* @return the builder for the request
*/
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI uri) {
return MockMvcRequestBuilders.request(httpMethod, uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a multipart request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the file upload request
*/
public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate,
Object... urlVariables) {
return (MockMultipartHttpServletRequestBuilder) MockMvcRequestBuilders
.fileUpload(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a multipart request.
*
* @param uri the URL
* @return the builder for the file upload request
*/
public static MockMultipartHttpServletRequestBuilder fileUpload(URI uri) {
return MockMvcRequestBuilders.fileUpload(uri);
}
}

View File

@@ -0,0 +1,134 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import static org.springframework.restdocs.mockmvc.util.IterableEnumeration.iterable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.restdocs.operation.StandardOperation;
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.util.Assert;
/**
* A Spring MVC Test {@code ResultHandler} for documenting RESTful APIs.
*
* @author Andy Wilkinson
* @author Andreas Evers
* @see MockMvcRestDocumentation#document(String, Snippet...)
*/
public class RestDocumentationResultHandler implements ResultHandler {
private final String identifier;
private final OperationRequestPreprocessor requestPreprocessor;
private final OperationResponsePreprocessor responsePreprocessor;
private final List<Snippet> snippets;
RestDocumentationResultHandler(String identifier, Snippet... snippets) {
this(identifier, new IdentityOperationRequestPreprocessor(),
new IdentityOperationResponsePreprocessor(), snippets);
}
RestDocumentationResultHandler(String identifier,
OperationRequestPreprocessor requestPreprocessor, Snippet... snippets) {
this(identifier, requestPreprocessor,
new IdentityOperationResponsePreprocessor(), snippets);
}
RestDocumentationResultHandler(String identifier,
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
this(identifier, new IdentityOperationRequestPreprocessor(),
responsePreprocessor, snippets);
}
RestDocumentationResultHandler(String identifier,
OperationRequestPreprocessor requestPreprocessor,
OperationResponsePreprocessor responsePreprocessor, Snippet... snippets) {
Assert.notNull(identifier, "identifier must be non-null");
Assert.notNull(requestPreprocessor, "requestPreprocessor must be non-null");
Assert.notNull(responsePreprocessor, "responsePreprocessor must be non-null");
Assert.notNull(snippets, "snippets must be non-null");
this.identifier = identifier;
this.requestPreprocessor = requestPreprocessor;
this.responsePreprocessor = responsePreprocessor;
this.snippets = Arrays.asList(snippets);
}
@Override
public void handle(MvcResult result) throws Exception {
Map<String, Object> attributes = new HashMap<>();
for (String name : iterable(result.getRequest().getAttributeNames())) {
attributes.put(name, result.getRequest().getAttribute(name));
}
OperationRequest request = this.requestPreprocessor
.preprocess(new MockMvcOperationRequestFactory()
.createOperationRequest(result.getRequest()));
OperationResponse response = this.responsePreprocessor
.preprocess(new MockMvcOperationResponseFactory()
.createOperationResponse(result.getResponse()));
Operation operation = new StandardOperation(this.identifier, request, response,
attributes);
for (Snippet snippet : getSnippets(result)) {
snippet.document(operation);
}
}
@SuppressWarnings("unchecked")
private List<Snippet> getSnippets(MvcResult result) {
List<Snippet> combinedSnippets = new ArrayList<>((List<Snippet>) result
.getRequest()
.getAttribute("org.springframework.restdocs.defaultSnippets"));
combinedSnippets.addAll(this.snippets);
return combinedSnippets;
}
static final class IdentityOperationRequestPreprocessor implements
OperationRequestPreprocessor {
@Override
public OperationRequest preprocess(OperationRequest request) {
return request;
}
}
static final class IdentityOperationResponsePreprocessor implements
OperationResponsePreprocessor {
@Override
public OperationResponse preprocess(OperationResponse response) {
return response;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import java.util.Arrays;
import java.util.List;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.restdocs.curl.CurlDocumentation;
import org.springframework.restdocs.http.HttpDocumentation;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.WriterResolver;
/**
* A configurer that can be used to configure the generated documentation snippets.
*
* @author Andy Wilkinson
*/
public class SnippetConfigurer extends
AbstractNestedConfigurer<RestDocumentationMockMvcConfigurer> {
private List<Snippet> defaultSnippets = Arrays.asList(
CurlDocumentation.curlRequest(), HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse());
/**
* The default encoding for documentation snippets
* @see #withEncoding(String)
*/
public static final String DEFAULT_SNIPPET_ENCODING = "UTF-8";
private String snippetEncoding = DEFAULT_SNIPPET_ENCODING;
SnippetConfigurer(RestDocumentationMockMvcConfigurer parent) {
super(parent);
}
/**
* Configures any documentation snippets to be written using the given
* {@code encoding}. The default is UTF-8.
* @param encoding the encoding
* @return {@code this}
*/
public SnippetConfigurer withEncoding(String encoding) {
this.snippetEncoding = encoding;
return this;
}
@Override
void apply(MockHttpServletRequest request) {
((WriterResolver) request.getAttribute(WriterResolver.class.getName()))
.setEncoding(this.snippetEncoding);
request.setAttribute("org.springframework.restdocs.defaultSnippets",
this.defaultSnippets);
}
/**
* Configures the documentation snippets that will be produced by default.
*
* @param defaultSnippets the default snippets
* @return {@code this}
*/
public SnippetConfigurer withDefaults(Snippet... defaultSnippets) {
this.defaultSnippets = Arrays.asList(defaultSnippets);
return this;
}
}

View File

@@ -0,0 +1,99 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import org.springframework.mock.web.MockHttpServletRequest;
/**
* A configurer that can be used to configure the documented URIs
*
* @author Andy Wilkinson
*/
public class UriConfigurer extends AbstractNestedConfigurer<RestDocumentationMockMvcConfigurer> {
/**
* The default scheme for documented URIs
* @see #withScheme(String)
*/
public static final String DEFAULT_SCHEME = "http";
/**
* The defalt host for documented URIs
* @see #withHost(String)
*/
public static final String DEFAULT_HOST = "localhost";
/**
* The default port for documented URIs
* @see #withPort(int)
*/
public static final int DEFAULT_PORT = 8080;
private String scheme = DEFAULT_SCHEME;
private String host = DEFAULT_HOST;
private int port = DEFAULT_PORT;
protected UriConfigurer(RestDocumentationMockMvcConfigurer parent) {
super(parent);
}
/**
* Configures any documented URIs to use the given {@code scheme}. The default is
* {@code http}.
*
* @param scheme The URI scheme
* @return {@code this}
*/
public UriConfigurer withScheme(String scheme) {
this.scheme = scheme;
return this;
}
/**
* Configures any documented URIs to use the given {@code host}. The default is
* {@code localhost}.
*
* @param host The URI host
* @return {@code this}
*/
public UriConfigurer withHost(String host) {
this.host = host;
return this;
}
/**
* Configures any documented URIs to use the given {@code port}. The default is
* {@code 8080}.
*
* @param port The URI port
* @return {@code this}
*/
public UriConfigurer withPort(int port) {
this.port = port;
return this;
}
@Override
void apply(MockHttpServletRequest request) {
request.setScheme(this.scheme);
request.setServerPort(this.port);
request.setServerName(this.host);
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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 org.springframework.restdocs.mockmvc.util;
import java.util.Enumeration;
import java.util.Iterator;
/**
* An adapter to expose an {@link Enumeration} as an {@link Iterable}.
*
* @author Andy Wilkinson
*
* @param <T> the type of the Enumeration's contents
*/
public final class IterableEnumeration<T> implements Iterable<T> {
private final Enumeration<T> enumeration;
private IterableEnumeration(Enumeration<T> enumeration) {
this.enumeration = enumeration;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return IterableEnumeration.this.enumeration.hasMoreElements();
}
@Override
public T next() {
return IterableEnumeration.this.enumeration.nextElement();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Creates a new {@code Iterable} that will iterate over the given {@code enumeration}
*
* @param <T> the type of the enumeration's elements
* @param enumeration The enumeration to expose as an {@code Iterable}
* @return the iterable
*/
public static <T> Iterable<T> iterable(Enumeration<T> enumeration) {
return new IterableEnumeration<T>(enumeration);
}
}

View File

@@ -0,0 +1,236 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.util.Arrays;
import javax.servlet.http.Part;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.mock.web.MockServletContext;
import org.springframework.restdocs.mockmvc.MockMvcOperationRequestFactory;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
/**
* Tests for {@link MockMvcOperationRequestFactory}
*
* @author Andy Wilkinson
*/
public class MockMvcOperationRequestFactoryTests {
private final MockMvcOperationRequestFactory factory = new MockMvcOperationRequestFactory();
@Test
public void httpRequest() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.get("/foo"));
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void httpRequestWithCustomPort() throws Exception {
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
.buildRequest(new MockServletContext());
mockRequest.setServerPort(8080);
OperationRequest request = this.factory.createOperationRequest(mockRequest);
assertThat(request.getUri(), is(URI.create("http://localhost:8080/foo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void requestWithContextPath() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders.get(
"/foo/bar").contextPath("/foo"));
assertThat(request.getUri(), is(URI.create("http://localhost/foo/bar")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void requestWithHeaders() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.get("/foo").header("a", "alpha", "apple").header("b", "bravo"));
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
assertThat(request.getHeaders(), hasEntry("a", Arrays.asList("alpha", "apple")));
assertThat(request.getHeaders(), hasEntry("b", Arrays.asList("bravo")));
}
@Test
public void httpsRequest() throws Exception {
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
.buildRequest(new MockServletContext());
mockRequest.setScheme("https");
mockRequest.setServerPort(443);
OperationRequest request = this.factory.createOperationRequest(mockRequest);
assertThat(request.getUri(), is(URI.create("https://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void httpsRequestWithCustomPort() throws Exception {
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
.buildRequest(new MockServletContext());
mockRequest.setScheme("https");
mockRequest.setServerPort(8443);
OperationRequest request = this.factory.createOperationRequest(mockRequest);
assertThat(request.getUri(), is(URI.create("https://localhost:8443/foo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void getRequestWithParametersProducesUriWithQueryString() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.get("/foo").param("a", "alpha", "apple").param("b", "br&vo"));
assertThat(request.getUri(),
is(URI.create("http://localhost/foo?a=alpha&a=apple&b=br%26vo")));
assertThat(request.getParameters().size(), is(2));
assertThat(request.getParameters(),
hasEntry("a", Arrays.asList("alpha", "apple")));
assertThat(request.getParameters(), hasEntry("b", Arrays.asList("br&vo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void getRequestWithQueryStringPopulatesParameters() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.get("/foo?a=alpha&b=bravo"));
assertThat(request.getUri(),
is(URI.create("http://localhost/foo?a=alpha&b=bravo")));
assertThat(request.getParameters().size(), is(2));
assertThat(request.getParameters(), hasEntry("a", Arrays.asList("alpha")));
assertThat(request.getParameters(), hasEntry("b", Arrays.asList("bravo")));
assertThat(request.getMethod(), is(HttpMethod.GET));
}
@Test
public void postRequestWithParameters() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.post("/foo").param("a", "alpha", "apple").param("b", "br&vo"));
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.POST));
assertThat(request.getParameters().size(), is(2));
assertThat(request.getParameters(),
hasEntry("a", Arrays.asList("alpha", "apple")));
assertThat(request.getParameters(), hasEntry("b", Arrays.asList("br&vo")));
}
@Test
public void mockMultipartFileUpload() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.fileUpload("/foo").file(
new MockMultipartFile("file", new byte[] { 1, 2, 3, 4 })));
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.POST));
assertThat(request.getParts().size(), is(1));
OperationRequestPart part = request.getParts().iterator().next();
assertThat(part.getName(), is(equalTo("file")));
assertThat(part.getSubmittedFileName(), is(nullValue()));
assertThat(part.getHeaders().isEmpty(), is(true));
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
}
@Test
public void mockMultipartFileUploadWithContentType() throws Exception {
OperationRequest request = createOperationRequest(MockMvcRequestBuilders
.fileUpload("/foo").file(
new MockMultipartFile("file", "original", "image/png",
new byte[] { 1, 2, 3, 4 })));
assertThat(request.getUri(), is(URI.create("http://localhost/foo")));
assertThat(request.getMethod(), is(HttpMethod.POST));
assertThat(request.getParts().size(), is(1));
OperationRequestPart part = request.getParts().iterator().next();
assertThat(part.getName(), is(equalTo("file")));
assertThat(part.getSubmittedFileName(), is(equalTo("original")));
assertThat(part.getHeaders().getContentType(), is(MediaType.IMAGE_PNG));
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
}
@Test
public void requestWithPart() throws Exception {
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
.buildRequest(new MockServletContext());
Part mockPart = mock(Part.class);
when(mockPart.getHeaderNames()).thenReturn(Arrays.asList("a", "b"));
when(mockPart.getHeaders("a")).thenReturn(Arrays.asList("alpha"));
when(mockPart.getHeaders("b")).thenReturn(Arrays.asList("bravo", "banana"));
when(mockPart.getInputStream()).thenReturn(
new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 }));
when(mockPart.getName()).thenReturn("part-name");
when(mockPart.getSubmittedFileName()).thenReturn("submitted.txt");
mockRequest.addPart(mockPart);
OperationRequest request = this.factory.createOperationRequest(mockRequest);
assertThat(request.getParts().size(), is(1));
OperationRequestPart part = request.getParts().iterator().next();
assertThat(part.getName(), is(equalTo("part-name")));
assertThat(part.getSubmittedFileName(), is(equalTo("submitted.txt")));
assertThat(part.getHeaders().getContentType(), is(nullValue()));
assertThat(part.getHeaders().get("a"), contains("alpha"));
assertThat(part.getHeaders().get("b"), contains("bravo", "banana"));
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
}
@Test
public void requestWithPartWithContentType() throws Exception {
MockHttpServletRequest mockRequest = MockMvcRequestBuilders.get("/foo")
.buildRequest(new MockServletContext());
Part mockPart = mock(Part.class);
when(mockPart.getHeaderNames()).thenReturn(Arrays.asList("a", "b"));
when(mockPart.getHeaders("a")).thenReturn(Arrays.asList("alpha"));
when(mockPart.getHeaders("b")).thenReturn(Arrays.asList("bravo", "banana"));
when(mockPart.getInputStream()).thenReturn(
new ByteArrayInputStream(new byte[] { 1, 2, 3, 4 }));
when(mockPart.getName()).thenReturn("part-name");
when(mockPart.getSubmittedFileName()).thenReturn("submitted.png");
when(mockPart.getContentType()).thenReturn("image/png");
mockRequest.addPart(mockPart);
OperationRequest request = this.factory.createOperationRequest(mockRequest);
assertThat(request.getParts().size(), is(1));
OperationRequestPart part = request.getParts().iterator().next();
assertThat(part.getName(), is(equalTo("part-name")));
assertThat(part.getSubmittedFileName(), is(equalTo("submitted.png")));
assertThat(part.getHeaders().getContentType(), is(MediaType.IMAGE_PNG));
assertThat(part.getHeaders().get("a"), contains("alpha"));
assertThat(part.getHeaders().get("b"), contains("bravo", "banana"));
assertThat(part.getContent(), is(equalTo(new byte[] { 1, 2, 3, 4 })));
}
private OperationRequest createOperationRequest(MockHttpServletRequestBuilder builder)
throws Exception {
return this.factory.createOperationRequest(builder
.buildRequest(new MockServletContext()));
}
}

View File

@@ -0,0 +1,378 @@
/*
* 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 org.springframework.restdocs.mockmvc;
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.curl.CurlDocumentation.curlRequest;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.maskLinks;
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.operation.preprocess.Preprocessors.replacePattern;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
import static org.springframework.restdocs.test.SnippetMatchers.snippet;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.restdocs.hypermedia.Link;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationIntegrationTests.TestConfiguration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* Integration tests for Spring REST Docs
*
* @author Andy Wilkinson
* @author Dewet Diener
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = TestConfiguration.class)
public class MockMvcRestDocumentationIntegrationTests {
@Rule
public RestDocumentation restDocumentation = new RestDocumentation(
"build/generated-snippets");
@Autowired
private WebApplicationContext context;
@Before
public void deleteSnippets() {
FileSystemUtils.deleteRecursively(new File("build/generated-snippets"));
}
@After
public void clearOutputDirSystemProperty() {
System.clearProperty("org.springframework.restdocs.outputDir");
}
@Test
public void basicSnippetGeneration() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("basic"));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/basic"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc");
}
@Test
public void linksSnippet() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("links",
links(linkWithRel("rel").description("The description"))));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
"links.adoc");
}
@Test
public void pathParametersSnippet() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("{foo}", "/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("links", pathParameters(parameterWithName("foo")
.description("The description"))));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
"path-parameters.adoc");
}
@Test
public void requestParametersSnippet() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("/").param("foo", "bar").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("links", requestParameters(parameterWithName("foo")
.description("The description"))));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
"request-parameters.adoc");
}
@Test
public void requestFieldsSnippet() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(
get("/").param("foo", "bar").content("{\"a\":\"alpha\"}")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("links",
requestFields(fieldWithPath("a").description("The description"))));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
"request-fields.adoc");
}
@Test
public void responseFieldsSnippet() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("/").param("foo", "bar").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document(
"links",
responseFields(
fieldWithPath("a").description("The description"),
fieldWithPath("links").description(
"Links to other resources"))));
assertExpectedSnippetFilesExist(new File("build/generated-snippets/links"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc",
"response-fields.adoc");
}
@Test
public void parameterizedOutputDirectory() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("{method-name}"));
assertExpectedSnippetFilesExist(new File(
"build/generated-snippets/parameterized-output-directory"),
"http-request.adoc", "http-response.adoc", "curl-request.adoc");
}
@Test
public void multiStep() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document("{method-name}-{step}")).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk());
assertExpectedSnippetFilesExist(
new File("build/generated-snippets/multi-step-1/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk());
assertExpectedSnippetFilesExist(
new File("build/generated-snippets/multi-step-2/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)).andExpect(
status().isOk());
assertExpectedSnippetFilesExist(
new File("build/generated-snippets/multi-step-3/"), "http-request.adoc",
"http-response.adoc", "curl-request.adoc");
}
@Test
public void preprocessedRequest() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
Pattern pattern = Pattern.compile("(\"alpha\")");
mockMvc.perform(
get("/").header("a", "alpha").header("b", "bravo")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON).content("{\"a\":\"alpha\"}"))
.andExpect(status().isOk())
.andDo(document("original-request"))
.andDo(document(
"preprocessed-request",
preprocessRequest(prettyPrint(), removeHeaders("a"),
replacePattern(pattern, "\"<<beta>>\""))));
assertThat(
new File("build/generated-snippets/original-request/http-request.adoc"),
is(snippet().withContents(
httpRequest(RequestMethod.GET, "/").header("Host", "localhost")
.header("a", "alpha").header("b", "bravo")
.header("Content-Type", "application/json")
.header("Accept", MediaType.APPLICATION_JSON_VALUE)
.header("Content-Length", "13")
.content("{\"a\":\"alpha\"}"))));
assertThat(
new File(
"build/generated-snippets/preprocessed-request/http-request.adoc"),
is(snippet().withContents(
httpRequest(RequestMethod.GET, "/").header("Host", "localhost")
.header("b", "bravo")
.header("Content-Type", "application/json")
.header("Accept", MediaType.APPLICATION_JSON_VALUE)
.header("Content-Length", "22")
.content(String.format("{%n \"a\" : \"<<beta>>\"%n}")))));
}
@Test
public void preprocessedResponse() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
Pattern pattern = Pattern.compile("(\"alpha\")");
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("original-response"))
.andDo(document(
"preprocessed-response",
preprocessResponse(prettyPrint(), maskLinks(),
removeHeaders("a"),
replacePattern(pattern, "\"<<beta>>\""))));
assertThat(
new File("build/generated-snippets/original-response/http-response.adoc"),
is(snippet().withContents(
httpResponse(HttpStatus.OK)
.header("a", "alpha")
.header("Content-Type", "application/json")
.content(
"{\"a\":\"alpha\",\"links\":[{\"rel\":\"rel\","
+ "\"href\":\"href\"}]}"))));
assertThat(
new File(
"build/generated-snippets/preprocessed-response/http-response.adoc"),
is(snippet().withContents(
httpResponse(HttpStatus.OK).header("Content-Type",
"application/json").content(
String.format("{%n \"a\" : \"<<beta>>\",%n \"links\" :"
+ " [ {%n \"rel\" : \"rel\",%n \"href\" :"
+ " \"...\"%n } ]%n}")))));
}
@Test
public void customSnippetTemplate() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)).build();
ClassLoader classLoader = new URLClassLoader(new URL[] { new File(
"src/test/resources/custom-snippet-templates").toURI().toURL() },
getClass().getClassLoader());
ClassLoader previous = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
try {
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("custom-snippet-template"));
}
finally {
Thread.currentThread().setContextClassLoader(previous);
}
assertThat(new File(
"build/generated-snippets/custom-snippet-template/curl-request.adoc"),
is(snippet().withContents(equalTo("Custom curl request"))));
mockMvc.perform(get("/")).andDo(
document(
"index",
curlRequest(attributes(key("title").value(
"Access the index using curl")))));
}
private void assertExpectedSnippetFilesExist(File directory, String... snippets) {
for (String snippet : snippets) {
assertTrue(new File(directory, snippet).isFile());
}
}
@Configuration
@EnableWebMvc
static class TestConfiguration extends WebMvcConfigurerAdapter {
@Bean
public TestController testController() {
return new TestController();
}
}
@RestController
static class TestController {
@RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Map<String, Object>> foo() {
Map<String, Object> response = new HashMap<>();
response.put("a", "alpha");
response.put("links", Arrays.asList(new Link("rel", "href")));
HttpHeaders headers = new HttpHeaders();
headers.add("a", "alpha");
return new ResponseEntity<Map<String, Object>>(response, headers,
HttpStatus.OK);
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.net.URI;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.hateoas.mvc.BasicLinkBuilder;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* Tests for {@link RestDocumentationMockMvcConfigurer}.
*
* @author Andy Wilkinson
* @author Dmitriy Mayboroda
*/
public class RestDocumentationConfigurerTests {
private MockHttpServletRequest request = new MockHttpServletRequest();
@Rule
public RestDocumentation restDocumentation = new RestDocumentation("test");
@Test
public void defaultConfiguration() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
assertUriConfiguration("http", "localhost", 8080);
}
@Test
public void customScheme() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).uris().withScheme("https")
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
assertUriConfiguration("https", "localhost", 8080);
}
@Test
public void customHost() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).uris().withHost("api.example.com")
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
assertUriConfiguration("http", "api.example.com", 8080);
}
@Test
public void customPort() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).uris().withPort(8081)
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
assertUriConfiguration("http", "localhost", 8081);
}
@Test
public void noContentLengthHeaderWhenRequestHasNotContent() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).uris().withPort(8081)
.beforeMockMvcCreated(null, null);
postProcessor.postProcessRequest(this.request);
assertThat(this.request.getHeader("Content-Length"), is(nullValue()));
}
@Test
public void contentLengthHeaderIsSetWhenRequestHasContent() {
RequestPostProcessor postProcessor = new RestDocumentationMockMvcConfigurer(
this.restDocumentation).beforeMockMvcCreated(null, null);
byte[] content = "Hello, world".getBytes();
this.request.setContent(content);
postProcessor.postProcessRequest(this.request);
assertThat(this.request.getHeader("Content-Length"),
is(equalTo(Integer.toString(content.length))));
}
private void assertUriConfiguration(String scheme, String host, int port) {
assertEquals(scheme, this.request.getScheme());
assertEquals(host, this.request.getServerName());
assertEquals(port, this.request.getServerPort());
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(
this.request));
try {
URI uri = BasicLinkBuilder.linkToCurrentMapping().toUri();
assertEquals(scheme, uri.getScheme());
assertEquals(host, uri.getHost());
assertEquals(port, uri.getPort());
}
finally {
RequestContextHolder.resetRequestAttributes();
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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 org.springframework.restdocs.mockmvc;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.fileUpload;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.head;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.options;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.request;
import java.net.URI;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
/**
* Tests for {@link RestDocumentationRequestBuilders}
*
* @author Andy Wilkinson
*
*/
public class RestDocumentationRequestBuildersTests {
private final ServletContext servletContext = new MockServletContext();
@Test
public void getTemplate() {
assertTemplate(get("{template}", "t"), HttpMethod.GET);
}
@Test
public void getUri() {
assertUri(get(URI.create("/uri")), HttpMethod.GET);
}
@Test
public void postTemplate() {
assertTemplate(post("{template}", "t"), HttpMethod.POST);
}
@Test
public void postUri() {
assertUri(post(URI.create("/uri")), HttpMethod.POST);
}
@Test
public void putTemplate() {
assertTemplate(put("{template}", "t"), HttpMethod.PUT);
}
@Test
public void putUri() {
assertUri(put(URI.create("/uri")), HttpMethod.PUT);
}
@Test
public void patchTemplate() {
assertTemplate(patch("{template}", "t"), HttpMethod.PATCH);
}
@Test
public void patchUri() {
assertUri(patch(URI.create("/uri")), HttpMethod.PATCH);
}
@Test
public void deleteTemplate() {
assertTemplate(delete("{template}", "t"), HttpMethod.DELETE);
}
@Test
public void deleteUri() {
assertUri(delete(URI.create("/uri")), HttpMethod.DELETE);
}
@Test
public void optionsTemplate() {
assertTemplate(options("{template}", "t"), HttpMethod.OPTIONS);
}
@Test
public void optionsUri() {
assertUri(options(URI.create("/uri")), HttpMethod.OPTIONS);
}
@Test
public void headTemplate() {
assertTemplate(head("{template}", "t"), HttpMethod.HEAD);
}
@Test
public void headUri() {
assertUri(head(URI.create("/uri")), HttpMethod.HEAD);
}
@Test
public void requestTemplate() {
assertTemplate(request(HttpMethod.GET, "{template}", "t"), HttpMethod.GET);
}
@Test
public void requestUri() {
assertUri(request(HttpMethod.GET, URI.create("/uri")), HttpMethod.GET);
}
@Test
public void fileUploadTemplate() {
assertTemplate(fileUpload("{template}", "t"), HttpMethod.POST);
}
@Test
public void fileUploadUri() {
assertUri(fileUpload(URI.create("/uri")), HttpMethod.POST);
}
private void assertTemplate(MockHttpServletRequestBuilder builder,
HttpMethod httpMethod) {
MockHttpServletRequest request = builder.buildRequest(this.servletContext);
assertThat(
(String) request.getAttribute("org.springframework.restdocs.urlTemplate"),
is(equalTo("{template}")));
assertThat(request.getRequestURI(), is(equalTo("t")));
assertThat(request.getMethod(), is(equalTo(httpMethod.name())));
}
private void assertUri(MockHttpServletRequestBuilder builder, HttpMethod httpMethod) {
MockHttpServletRequest request = builder.buildRequest(this.servletContext);
assertThat(request.getRequestURI(), is(equalTo("/uri")));
assertThat(request.getMethod(), is(equalTo(httpMethod.name())));
}
}

View File

@@ -0,0 +1,154 @@
/*
* 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 org.springframework.restdocs.mockmvc.test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
import org.springframework.restdocs.snippet.StandardWriterResolver;
import org.springframework.restdocs.snippet.WriterResolver;
import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.web.servlet.FlashMap;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* A minimal stub implementation of {@link MvcResult}
*
* @author Andy Wilkinson
*
*/
public class StubMvcResult implements MvcResult {
private final MockHttpServletRequest request;
private final MockHttpServletResponse response;
public static StubMvcResult result() {
return new StubMvcResult();
}
public static StubMvcResult result(RequestBuilder requestBuilder) {
return new StubMvcResult(requestBuilder);
}
public static StubMvcResult result(RequestBuilder requestBuilder,
MockHttpServletResponse response) {
return new StubMvcResult(requestBuilder, response);
}
public static StubMvcResult result(MockHttpServletRequest request) {
return new StubMvcResult(request);
}
public static StubMvcResult result(MockHttpServletResponse response) {
return new StubMvcResult(response);
}
public static StubMvcResult result(MockHttpServletRequest request,
MockHttpServletResponse response) {
return new StubMvcResult(request, response);
}
private StubMvcResult() {
this(new MockHttpServletRequest(), new MockHttpServletResponse());
}
private StubMvcResult(MockHttpServletRequest request) {
this(request, new MockHttpServletResponse());
}
private StubMvcResult(MockHttpServletResponse response) {
this(new MockHttpServletRequest(), response);
}
private StubMvcResult(RequestBuilder requestBuilder, MockHttpServletResponse response) {
this(requestBuilder.buildRequest(new MockServletContext()), response);
}
private StubMvcResult(MockHttpServletRequest request, MockHttpServletResponse response) {
this.request = request;
if (this.request.getAttribute(TemplateEngine.class.getName()) == null) {
this.request.setAttribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(new StandardTemplateResourceResolver()));
}
RestDocumentationContext context = new RestDocumentationContext(null, null, null);
this.request.setAttribute(RestDocumentationContext.class.getName(), context);
if (this.request.getAttribute(WriterResolver.class.getName()) == null) {
this.request.setAttribute(WriterResolver.class.getName(),
new StandardWriterResolver(
new RestDocumentationContextPlaceholderResolver(context)));
}
this.response = response;
}
private StubMvcResult(RequestBuilder requestBuilder) {
this(requestBuilder.buildRequest(new MockServletContext()));
}
@Override
public MockHttpServletRequest getRequest() {
return this.request;
}
@Override
public MockHttpServletResponse getResponse() {
return this.response;
}
@Override
public Object getHandler() {
return null;
}
@Override
public HandlerInterceptor[] getInterceptors() {
return null;
}
@Override
public ModelAndView getModelAndView() {
return null;
}
@Override
public Exception getResolvedException() {
return null;
}
@Override
public FlashMap getFlashMap() {
return null;
}
@Override
public Object getAsyncResult() {
return null;
}
@Override
public Object getAsyncResult(long timeToWait) {
return null;
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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 org.springframework.restdocs.mockmvc.test;
import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
public class TestRequestBuilders {
private TestRequestBuilders() {
}
public static MockHttpServletRequestBuilder get(String urlTemplate,
Object... urlVariables) {
return org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get(
urlTemplate, urlVariables).requestAttr(
RestDocumentationContext.class.getName(),
new RestDocumentationContext(null, null, null));
}
}