Add support for generating snippets in Markdown

This commit introduces support for generating snippets formatted
using Markdown. Asciidoctor remains the default.

A new SnippetFormat abstraction has been introduced with Asciidoctor
and Markdown implementations provided out of the box.
Markdown-formatted templates are also provided for all of the default
snippets.

Please refer to the updated reference documentation for further
details.

Closes gh-150
Closes gh-19
This commit is contained in:
Andy Wilkinson
2016-01-29 16:10:00 +00:00
parent b34d2dd443
commit 4d37dea1d6
99 changed files with 2100 additions and 1037 deletions

View File

@@ -73,7 +73,7 @@
<module name="AvoidStarImport" />
<module name="AvoidStaticImport">
<property name="excludes"
value="com.jayway.restassured.RestAssured.*, org.junit.Assert.*, org.junit.Assume.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.springframework.restdocs.curl.CurlDocumentation.*, org.springframework.restdocs.headers.HeaderDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.IterableEnumeration.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*, org.springframework.restdocs.payload.PayloadDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.request.RequestDocumentation.*, org.springframework.restdocs.restassured.RestAssuredRestDocumentation.*, org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.snippet.Attributes.*, org.springframework.restdocs.test.SnippetMatchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*" />
value="com.jayway.restassured.RestAssured.*, org.junit.Assert.*, org.junit.Assume.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.Matchers.*, org.springframework.restdocs.curl.CurlDocumentation.*, org.springframework.restdocs.headers.HeaderDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.IterableEnumeration.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*, org.springframework.restdocs.payload.PayloadDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.request.RequestDocumentation.*, org.springframework.restdocs.restassured.RestAssuredRestDocumentation.*, org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.snippet.Attributes.*, org.springframework.restdocs.snippet.SnippetFormats.*, org.springframework.restdocs.test.SnippetMatchers.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*" />
</module>
<module name="IllegalImport" />
<module name="RedundantImport" />

View File

@@ -42,8 +42,7 @@ TIP: To configure a request's context path, use the `contextPath` method on
[[configuration-snippet-encoding]]
=== Snippet encoding
The default encoding used by Asciidoctor is `UTF-8`. Spring REST Docs adopts the same
default for the snippets that it generates. You can change the default snippet encoding
The default snippet encoding is `UTF-8`. You can change the default snippet encoding
using the `RestDocumentationConfigurer` API. For example, to use `ISO-8859-1`:
[source,java,indent=0,role="primary"]
@@ -60,6 +59,26 @@ include::{examples-dir}/com/example/restassured/CustomEncoding.java[tags=custom-
[[configuration-snippet-format]]
=== Snippet format
The default snippet format is Asciidoctor. Markdown is also supported out of the box. You
can change the default format using the `RestDocumentationConfigurer` API:
[source,java,indent=0,role="primary"]
.MockMvc
----
include::{examples-dir}/com/example/mockmvc/CustomFormat.java[tags=custom-format]
----
[source,java,indent=0,role="secondary"]
.REST Assured
----
include::{examples-dir}/com/example/restassured/CustomFormat.java[tags=custom-format]
----
[[configuration-default-snippets]]
=== Default snippets

View File

@@ -26,4 +26,5 @@ include::documenting-your-api.adoc[]
include::customizing-requests-and-responses.adoc[]
include::configuration.adoc[]
include::working-with-asciidoctor.adoc[]
include::working-with-markdown.adoc[]
include::contributing.adoc[]

View File

@@ -6,8 +6,9 @@ services that is accurate and readable.
Writing high-quality documentation is difficult. One way to ease that difficulty is to use
tools that are well-suited to the job. To this end, Spring REST Docs uses
http://asciidoctor.org[Asciidoctor]. Asciidoctor processes plain text and produces
HTML, styled and layed out to suit your needs.
http://asciidoctor.org[Asciidoctor] by default. Asciidoctor processes plain text and
produces HTML, styled and layed out to suit your needs. If you prefer, Spring REST Docs
can also be configured to use Markdown.
Spring REST Docs makes use of snippets produced by tests written with
{spring-framework-docs}/#spring-mvc-test-framework[Spring MVC Test] or

View File

@@ -0,0 +1,28 @@
[[working-with-markdown]]
== Working with Markdown
This section describes any aspects of working with Markdown that are particularly
relevant to Spring REST Docs.
[[working-with-markdown-limitations]]
=== Limitations
Markdown was originally designed for people writing for the web and, as such, isn't
as well-suited to writing documentation as Asciidoctor. Typically, these limitations
are overcome by using another tool that builds on top of Markdown.
Markdown has no official support for tables. Spring REST Docs' default Markdown snippet
templates use https://michelf.ca/projects/php-markdown/extra/#table[Markdown Extra's table
format].
[[working-with-markdown-including-snippets]]
=== Including snippets
Markdown has no built-in support for including one Markdown file in another. To include
the generated snippets of Markdown in your documentation, you should use an additional
tool that supports this functionality. One example that's particularly well-suited to
documenting APIs is https://github.com/tripit/slate[Slate].

View File

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

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.restassured;
import org.junit.Before;
import org.junit.Rule;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.restdocs.snippet.SnippetFormats;
import com.jayway.restassured.builder.RequestSpecBuilder;
import com.jayway.restassured.specification.RequestSpecification;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
public class CustomFormat {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build");
private RequestSpecification spec;
@Before
public void setUp() {
// tag::custom-format[]
this.spec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(this.restDocumentation)
.snippets().withFormat(SnippetFormats.markdown()))
.build();
// end::custom-format[]
}
}

View File

@@ -93,13 +93,20 @@ public abstract class RestDocumentationConfigurer<S, T> {
private static final class TemplateEngineConfigurer extends AbstractConfigurer {
private TemplateEngine templateEngine = new MustacheTemplateEngine(
new StandardTemplateResourceResolver());
private TemplateEngine templateEngine;
@Override
public void apply(Map<String, Object> configuration,
RestDocumentationContext context) {
configuration.put(TemplateEngine.class.getName(), this.templateEngine);
TemplateEngine engineToUse = this.templateEngine;
if (engineToUse == null) {
SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration
.get(SnippetConfiguration.class.getName());
engineToUse = new MustacheTemplateEngine(
new StandardTemplateResourceResolver(
snippetConfiguration.getFormat()));
}
configuration.put(TemplateEngine.class.getName(), engineToUse);
}
private void setTemplateEngine(TemplateEngine templateEngine) {
@@ -117,8 +124,12 @@ public abstract class RestDocumentationConfigurer<S, T> {
RestDocumentationContext context) {
WriterResolver resolverToUse = this.writerResolver;
if (resolverToUse == null) {
SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration
.get(SnippetConfiguration.class.getName());
resolverToUse = new StandardWriterResolver(
new RestDocumentationContextPlaceholderResolver(context));
new RestDocumentationContextPlaceholderResolver(context),
snippetConfiguration.getEncoding(),
snippetConfiguration.getFormat());
}
configuration.put(WriterResolver.class.getName(), resolverToUse);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.config;
import org.springframework.restdocs.snippet.SnippetFormat;
/**
* An encapsulation of the configuration for documentation snippets.
*
* @author Andy Wilkinson
*/
class SnippetConfiguration {
private final String encoding;
private final SnippetFormat format;
SnippetConfiguration(String encoding, SnippetFormat format) {
this.encoding = encoding;
this.format = format;
}
String getEncoding() {
return this.encoding;
}
SnippetFormat getFormat() {
return this.format;
}
}

View File

@@ -24,7 +24,8 @@ import org.springframework.restdocs.RestDocumentationContext;
import org.springframework.restdocs.curl.CurlDocumentation;
import org.springframework.restdocs.http.HttpDocumentation;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.WriterResolver;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.snippet.SnippetFormats;
/**
* A configurer that can be used to configure the generated documentation snippets.
@@ -51,8 +52,18 @@ public abstract class SnippetConfigurer<P, T> extends AbstractNestedConfigurer<P
*/
public static final String DEFAULT_SNIPPET_ENCODING = "UTF-8";
/**
* The default format for documentation snippets.
*
* @see #withFormat(SnippetFormat)
*/
public static final SnippetFormat DEFAULT_SNIPPET_FORMAT = SnippetFormats
.asciidoctor();
private String snippetEncoding = DEFAULT_SNIPPET_ENCODING;
private SnippetFormat snippetFormat = DEFAULT_SNIPPET_FORMAT;
/**
* Creates a new {@code SnippetConfigurer} with the given {@code parent}.
*
@@ -64,8 +75,8 @@ public abstract class SnippetConfigurer<P, T> extends AbstractNestedConfigurer<P
@Override
public void apply(Map<String, Object> configuration, RestDocumentationContext context) {
((WriterResolver) configuration.get(WriterResolver.class.getName()))
.setEncoding(this.snippetEncoding);
configuration.put(SnippetConfiguration.class.getName(), new SnippetConfiguration(
this.snippetEncoding, this.snippetFormat));
configuration.put(ATTRIBUTE_DEFAULT_SNIPPETS, this.defaultSnippets);
}
@@ -93,4 +104,17 @@ public abstract class SnippetConfigurer<P, T> extends AbstractNestedConfigurer<P
this.defaultSnippets = Arrays.asList(defaultSnippets);
return (T) this;
}
/**
* Configures the format of the documentation snippets.
*
* @param format the snippet format
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public T withFormat(SnippetFormat format) {
this.snippetFormat = format;
return (T) this;
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.snippet;
/**
* A {@link SnippetFormat} provides information about a particular snippet format, such as
* Asciidoctor or Markdown.
*
* @author Andy Wilkinson
*/
public interface SnippetFormat {
/**
* Returns the snippet format's file extension.
*
* @return the file extension
*/
String getFileExtension();
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.snippet;
/**
* An enumeration of the built-in snippet formats.
*
* @author Andy Wilkinson
*/
public abstract class SnippetFormats {
private static final SnippetFormat ASCIIDOCTOR = new AsciidoctorSnippetFormat();
private static final SnippetFormat MARKDOWN = new MarkdownSnippetFormat();
private SnippetFormats() {
}
/**
* Returns the Asciidoctor snippet format.
*
* @return the snippet format
*/
public static SnippetFormat asciidoctor() {
return ASCIIDOCTOR;
}
/**
* Returns the Markdown snippet format.
*
* @return the snippet format
*/
public static SnippetFormat markdown() {
return MARKDOWN;
}
private static final class AsciidoctorSnippetFormat implements SnippetFormat {
private static final String FILE_EXTENSION = "adoc";
@Override
public String getFileExtension() {
return FILE_EXTENSION;
}
}
private static final class MarkdownSnippetFormat implements SnippetFormat {
private static final String FILE_EXTENSION = "md";
@Override
public String getFileExtension() {
return FILE_EXTENSION;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,29 +33,54 @@ import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
*/
public final class StandardWriterResolver implements WriterResolver {
private String encoding = "UTF-8";
private final PlaceholderResolver placeholderResolver;
private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper(
"{", "}");
private String encoding = "UTF-8";
private SnippetFormat snippetFormat;
/**
* Creates a new {@code StandardWriterResolver} that will use the given
* {@code placeholderResolver} to resolve any placeholders in the
* {@code operationName}.
* {@code operationName}. Writers will use {@code UTF-8} encoding and, when writing to
* a file, will use a filename appropriate for Asciidoctor content.
*
* @param placeholderResolver the placeholder resolver
* @deprecated since 1.1.0 in favor of
* {@link #StandardWriterResolver(PropertyPlaceholderHelper.PlaceholderResolver, String, SnippetFormat)}
*/
@Deprecated
public StandardWriterResolver(PlaceholderResolver placeholderResolver) {
this(placeholderResolver, "UTF-8", SnippetFormats.asciidoctor());
}
/**
* Creates a new {@code StandardWriterResolver} that will use the given
* {@code placeholderResolver} to resolve any placeholders in the
* {@code operationName}. Writers will use the given {@code encoding} and, when
* writing to a file, will use a filename appropriate for content in the given
* {@code snippetFormat}.
*
* @param placeholderResolver the placeholder resolver
* @param encoding the encoding
* @param snippetFormat the snippet format
*/
public StandardWriterResolver(PlaceholderResolver placeholderResolver,
String encoding, SnippetFormat snippetFormat) {
this.placeholderResolver = placeholderResolver;
this.encoding = encoding;
this.snippetFormat = snippetFormat;
}
@Override
public Writer resolve(String operationName, String snippetName,
RestDocumentationContext context) throws IOException {
File outputFile = resolveFile(this.propertyPlaceholderHelper.replacePlaceholders(
operationName, this.placeholderResolver), snippetName + ".adoc", context);
operationName, this.placeholderResolver), snippetName + "."
+ this.snippetFormat.getFileExtension(), context);
if (outputFile != null) {
createDirectoriesIfNecessary(outputFile);
@@ -95,4 +120,5 @@ public final class StandardWriterResolver implements WriterResolver {
throw new IllegalStateException("Failed to create directory '" + parent + "'");
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,7 +47,10 @@ public interface WriterResolver {
* resolver.
*
* @param encoding the encoding
* @deprecated since 1.1.0 in favour of configuring the encoding when to resolver is
* created
*/
@Deprecated
void setEncoding(String encoding);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,27 +18,54 @@ package org.springframework.restdocs.templates;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.snippet.SnippetFormats;
/**
* Standard implementation of {@link TemplateResourceResolver}.
* <p>
* Templates are resolved by first looking for a resource on the classpath named
* Templates are resolved by looking for a resource on the classpath named
* {@code org/springframework/restdocs/templates/&#123;name&#125;.snippet}. If no such
* resource exists {@code default-} is prepended to the name and the classpath is checked
* again. The built-in snippet templates are all named {@code default- name}, thereby
* allowing them to be overridden.
* resource exists an attempt is made to return a default resource that is appropriate for
* the configured snippet format.
*
* @author Andy Wilkinson
*/
public class StandardTemplateResourceResolver implements TemplateResourceResolver {
private final SnippetFormat snippetFormat;
/**
* Creates a new {@code StandardTemplateResourceResolver} that will produce default
* template resources formatted with Asciidoctor.
*
* @deprecated since 1.1.0 in favour of
* {@link #StandardTemplateResourceResolver(SnippetFormat)}
*/
@Deprecated
public StandardTemplateResourceResolver() {
this(SnippetFormats.asciidoctor());
}
/**
* Creates a new {@code StandardTemplateResourceResolver} that will produce default
* template resources formatted with the given {@code snippetFormat}.
*
* @param snippetFormat the format for the default snippet templates
*/
public StandardTemplateResourceResolver(SnippetFormat snippetFormat) {
this.snippetFormat = snippetFormat;
}
@Override
public Resource resolveTemplateResource(String name) {
ClassPathResource classPathResource = new ClassPathResource(
"org/springframework/restdocs/templates/" + name + ".snippet");
if (!classPathResource.exists()) {
classPathResource = new ClassPathResource(
"org/springframework/restdocs/templates/default-" + name + ".snippet");
"org/springframework/restdocs/templates/"
+ this.snippetFormat.getFileExtension() + "/" + name
+ ".snippet");
if (!classPathResource.exists()) {
throw new IllegalStateException("Template named '" + name
+ "' could not be resolved");

View File

@@ -0,0 +1,3 @@
```bash
$ curl {{url}} {{options}}
```

View File

@@ -0,0 +1,7 @@
```http
{{method}} {{path}} HTTP/1.1
{{#headers}}
{{name}}: {{value}}
{{/headers}}
{{requestBody}}
```

View File

@@ -0,0 +1,7 @@
```http
HTTP/1.1 {{statusCode}} {{statusReason}}
{{#headers}}
{{name}}: {{value}}
{{/headers}}
{{responseBody}}
```

View File

@@ -0,0 +1,5 @@
Relation | Description
-------- | -----------
{{#links}}
{{rel}} | {{description}}
{{/links}}

View File

@@ -0,0 +1,6 @@
{{path}}
Parameter | Description
--------- | -----------
{{#parameters}}
{{name}} | {{description}}
{{/parameters}}

View File

@@ -0,0 +1,5 @@
Path | Type | Description
---- | ---- | -----------
{{#fields}}
{{path}} | {{type}} | {{description}}
{{/fields}}

View File

@@ -0,0 +1,5 @@
Name | Description
---- | -----------
{{#headers}}
{{name}} | {{description}}
{{/headers}}

View File

@@ -0,0 +1,5 @@
Parameter | Description
--------- | -----------
{{#parameters}}
{{name}} | {{description}}
{{/parameters}}

View File

@@ -0,0 +1,5 @@
Path | Type | Description
---- | ---- | -----------
{{#fields}}
{{path}} | {{type}} | {{description}}
{{/fields}}

View File

@@ -0,0 +1,5 @@
Name | Description
---- | -----------
{{#headers}}
{{name}} | {{description}}
{{/headers}}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpStatus;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import org.springframework.restdocs.test.SnippetMatchers;
import org.springframework.restdocs.test.SnippetMatchers.CodeBlockMatcher;
import org.springframework.restdocs.test.SnippetMatchers.HttpRequestMatcher;
import org.springframework.restdocs.test.SnippetMatchers.HttpResponseMatcher;
import org.springframework.restdocs.test.SnippetMatchers.TableMatcher;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor;
import static org.springframework.restdocs.snippet.SnippetFormats.markdown;
/**
* Abstract base class for testing snippet generation.
*
* @author Andy Wilkinson
*/
@RunWith(Parameterized.class)
public abstract class AbstractSnippetTests {
protected final SnippetFormat snippetFormat;
@Rule
public ExpectedSnippet snippet;
@Parameters(name = "{0}")
public static List<Object[]> parameters() {
return Arrays.asList(new Object[] { "Asciidoctor", asciidoctor() }, new Object[] {
"Markdown", markdown() });
}
public AbstractSnippetTests(String name, SnippetFormat snippetFormat) {
this.snippet = new ExpectedSnippet(snippetFormat);
this.snippetFormat = snippetFormat;
}
public CodeBlockMatcher<?> codeBlock(String language) {
return SnippetMatchers.codeBlock(this.snippetFormat, language);
}
public TableMatcher<?> tableWithHeader(String... headers) {
return SnippetMatchers.tableWithHeader(this.snippetFormat, headers);
}
public TableMatcher<?> tableWithTitleAndHeader(String title, String... headers) {
return SnippetMatchers
.tableWithTitleAndHeader(this.snippetFormat, title, headers);
}
public HttpRequestMatcher httpRequest(RequestMethod method, String uri) {
return SnippetMatchers.httpRequest(this.snippetFormat, method, uri);
}
public HttpResponseMatcher httpResponse(HttpStatus responseStatus) {
return SnippetMatchers.httpResponse(this.snippetFormat, responseStatus);
}
public OperationBuilder operationBuilder(String name) {
return new OperationBuilder(name, this.snippet.getOutputDirectory(),
this.snippetFormat);
}
protected FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ this.snippetFormat.getFileExtension() + "/" + name + ".snippet");
}
}

View File

@@ -18,17 +18,16 @@ package org.springframework.restdocs.curl;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import org.springframework.util.Base64Utils;
import static org.hamcrest.CoreMatchers.containsString;
@@ -36,7 +35,6 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
/**
* Tests for {@link CurlRequestSnippet}.
@@ -47,30 +45,27 @@ import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
* @author Jonathan Pearlin
* @author Paul-Christian Volkmer
*/
public class CurlRequestSnippetTests {
@RunWith(Parameterized.class)
public class CurlRequestSnippetTests extends AbstractSnippetTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet();
@Rule
public ExpectedException thrown = ExpectedException.none();
public CurlRequestSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void getRequest() throws IOException {
this.snippet.expectCurlRequest("get-request").withContents(
codeBlock("bash").content("$ curl 'http://localhost/foo' -i"));
new CurlRequestSnippet().document(new OperationBuilder("get-request",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
.build());
new CurlRequestSnippet().document(operationBuilder("get-request").request(
"http://localhost/foo").build());
}
@Test
public void nonGetRequest() throws IOException {
this.snippet.expectCurlRequest("non-get-request").withContents(
codeBlock("bash").content("$ curl 'http://localhost/foo' -i -X POST"));
new CurlRequestSnippet().document(new OperationBuilder("non-get-request",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
.method("POST").build());
new CurlRequestSnippet().document(operationBuilder("non-get-request")
.request("http://localhost/foo").method("POST").build());
}
@Test
@@ -78,9 +73,8 @@ public class CurlRequestSnippetTests {
this.snippet.expectCurlRequest("request-with-content").withContents(
codeBlock("bash")
.content("$ curl 'http://localhost/foo' -i -d 'content'"));
new CurlRequestSnippet().document(new OperationBuilder("request-with-content",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
.content("content").build());
new CurlRequestSnippet().document(operationBuilder("request-with-content")
.request("http://localhost/foo").content("content").build());
}
@Test
@@ -89,8 +83,7 @@ public class CurlRequestSnippetTests {
.withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo?param=value' -i"));
new CurlRequestSnippet().document(new OperationBuilder(
"request-with-query-string", this.snippet.getOutputDirectory()).request(
new CurlRequestSnippet().document(operationBuilder("request-with-query-string").request(
"http://localhost/foo?param=value").build());
}
@@ -99,8 +92,7 @@ public class CurlRequestSnippetTests {
this.snippet.expectCurlRequest("post-request-with-query-string").withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo?param=value' -i -X POST"));
new CurlRequestSnippet().document(new OperationBuilder(
"post-request-with-query-string", this.snippet.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("post-request-with-query-string")
.request("http://localhost/foo?param=value").method("POST").build());
}
@@ -110,8 +102,7 @@ public class CurlRequestSnippetTests {
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'"));
new CurlRequestSnippet()
.document(new OperationBuilder("post-request-with-one-parameter",
this.snippet.getOutputDirectory())
.document(operationBuilder("post-request-with-one-parameter")
.request("http://localhost/foo").method("POST").param("k1", "v1")
.build());
}
@@ -123,10 +114,10 @@ public class CurlRequestSnippetTests {
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X POST"
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
new CurlRequestSnippet().document(new OperationBuilder(
"post-request-with-multiple-parameters", this.snippet
.getOutputDirectory()).request("http://localhost/foo")
.method("POST").param("k1", "v1", "v1-bis").param("k2", "v2").build());
new CurlRequestSnippet()
.document(operationBuilder("post-request-with-multiple-parameters")
.request("http://localhost/foo").method("POST")
.param("k1", "v1", "v1-bis").param("k2", "v2").build());
}
@Test
@@ -136,10 +127,10 @@ public class CurlRequestSnippetTests {
.withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'"));
new CurlRequestSnippet().document(new OperationBuilder(
"post-request-with-url-encoded-parameter", this.snippet
.getOutputDirectory()).request("http://localhost/foo")
.method("POST").param("k1", "a&b").build());
new CurlRequestSnippet().document(operationBuilder(
"post-request-with-url-encoded-parameter")
.request("http://localhost/foo").method("POST").param("k1", "a&b")
.build());
}
@Test
@@ -150,10 +141,10 @@ public class CurlRequestSnippetTests {
codeBlock("bash")
.content(
"$ curl 'http://localhost/foo?a=alpha' -i -X POST -d 'b=bravo'"));
new CurlRequestSnippet().document(new OperationBuilder(
"post-request-with-query-string-and-parameter", this.snippet
.getOutputDirectory()).request("http://localhost/foo?a=alpha")
.method("POST").param("b", "bravo").build());
new CurlRequestSnippet().document(operationBuilder(
"post-request-with-query-string-and-parameter")
.request("http://localhost/foo?a=alpha").method("POST")
.param("b", "bravo").build());
}
@Test
@@ -165,10 +156,10 @@ public class CurlRequestSnippetTests {
codeBlock("bash")
.content(
"$ curl 'http://localhost/foo?a=alpha' -i -X POST -d 'b=bravo'"));
new CurlRequestSnippet().document(new OperationBuilder(
"post-request-with-overlapping-query-string-and-parameters", this.snippet
.getOutputDirectory()).request("http://localhost/foo?a=alpha")
.method("POST").param("a", "alpha").param("b", "bravo").build());
new CurlRequestSnippet().document(operationBuilder(
"post-request-with-overlapping-query-string-and-parameters")
.request("http://localhost/foo?a=alpha").method("POST")
.param("a", "alpha").param("b", "bravo").build());
}
@Test
@@ -176,8 +167,7 @@ public class CurlRequestSnippetTests {
this.snippet.expectCurlRequest("put-request-with-one-parameter").withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'"));
new CurlRequestSnippet().document(new OperationBuilder(
"put-request-with-one-parameter", this.snippet.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("put-request-with-one-parameter")
.request("http://localhost/foo").method("PUT").param("k1", "v1").build());
}
@@ -188,11 +178,9 @@ public class CurlRequestSnippetTests {
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X PUT"
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
new CurlRequestSnippet()
.document(new OperationBuilder("put-request-with-multiple-parameters",
this.snippet.getOutputDirectory())
.request("http://localhost/foo").method("PUT").param("k1", "v1")
.param("k1", "v1-bis").param("k2", "v2").build());
new CurlRequestSnippet().document(operationBuilder("put-request-with-multiple-parameters")
.request("http://localhost/foo").method("PUT").param("k1", "v1")
.param("k1", "v1-bis").param("k2", "v2").build());
}
@Test
@@ -201,9 +189,8 @@ public class CurlRequestSnippetTests {
.withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'"));
new CurlRequestSnippet().document(new OperationBuilder(
"put-request-with-url-encoded-parameter", this.snippet
.getOutputDirectory()).request("http://localhost/foo")
new CurlRequestSnippet().document(operationBuilder(
"put-request-with-url-encoded-parameter").request("http://localhost/foo")
.method("PUT").param("k1", "a&b").build());
}
@@ -213,8 +200,8 @@ public class CurlRequestSnippetTests {
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i"
+ " -H 'Content-Type: application/json' -H 'a: alpha'"));
new CurlRequestSnippet().document(new OperationBuilder("request-with-headers",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
new CurlRequestSnippet().document(operationBuilder("request-with-headers")
.request("http://localhost/foo")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header("a", "alpha").build());
}
@@ -226,8 +213,7 @@ public class CurlRequestSnippetTests {
+ "'metadata={\"description\": \"foo\"}'";
this.snippet.expectCurlRequest("multipart-post-no-original-filename")
.withContents(codeBlock("bash").content(expectedContent));
new CurlRequestSnippet().document(new OperationBuilder(
"multipart-post-no-original-filename", this.snippet.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("multipart-post-no-original-filename")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("metadata", "{\"description\": \"foo\"}".getBytes()).build());
@@ -240,8 +226,7 @@ public class CurlRequestSnippetTests {
+ "'image=@documents/images/example.png;type=image/png'";
this.snippet.expectCurlRequest("multipart-post-with-content-type").withContents(
codeBlock("bash").content(expectedContent));
new CurlRequestSnippet().document(new OperationBuilder(
"multipart-post-with-content-type", this.snippet.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("multipart-post-with-content-type")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("image", new byte[0])
@@ -256,9 +241,8 @@ public class CurlRequestSnippetTests {
+ "'image=@documents/images/example.png'";
this.snippet.expectCurlRequest("multipart-post").withContents(
codeBlock("bash").content(expectedContent));
new CurlRequestSnippet().document(new OperationBuilder("multipart-post",
this.snippet.getOutputDirectory()).request("http://localhost/upload")
.method("POST")
new CurlRequestSnippet().document(operationBuilder("multipart-post")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("image", new byte[0])
.submittedFileName("documents/images/example.png").build());
@@ -272,8 +256,7 @@ public class CurlRequestSnippetTests {
+ "-F 'b=banana'";
this.snippet.expectCurlRequest("multipart-post-with-parameters").withContents(
codeBlock("bash").content(expectedContent));
new CurlRequestSnippet().document(new OperationBuilder(
"multipart-post-with-parameters", this.snippet.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("multipart-post-with-parameters")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("image", new byte[0])
@@ -281,34 +264,30 @@ public class CurlRequestSnippetTests {
.param("a", "apple", "avocado").param("b", "banana").build());
}
@Test
public void customAttributes() throws IOException {
this.snippet.expectCurlRequest("custom-attributes").withContents(
containsString("curl request title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("curl-request"))
.willReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/curl-request-with-title.snippet"));
new CurlRequestSnippet(attributes(key("title").value("curl request title")))
.document(new OperationBuilder("custom-attributes", this.snippet
.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost/foo").build());
}
@Test
public void basicAuthCredentialsAreSuppliedUsingUserOption() throws IOException {
this.snippet.expectCurlRequest("basic-auth").withContents(
codeBlock("bash").content(
"$ curl 'http://localhost/foo' -i -u 'user:secret'"));
new CurlRequestSnippet().document(new OperationBuilder("basic-auth", this.snippet
.getOutputDirectory())
new CurlRequestSnippet().document(operationBuilder("basic-auth")
.request("http://localhost/foo")
.header(HttpHeaders.AUTHORIZATION,
"Basic " + Base64Utils.encodeToString("user:secret".getBytes()))
.build());
}
@Test
public void customAttributes() throws IOException {
this.snippet.expectCurlRequest("custom-attributes").withContents(
containsString("curl request title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("curl-request")).willReturn(
snippetResource("curl-request-with-title"));
new CurlRequestSnippet(attributes(key("title").value("curl request title")))
.document(operationBuilder("custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost/foo").build());
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.headers;
import java.io.IOException;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
/**
* Tests for failures when rendering {@link RequestHeadersSnippet} due to missing or
* undocumented headers.
*
* @author Andy Wilkinson
*/
public class RequestHeadersSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void missingRequestHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Headers with the following names were not found"
+ " in the request: [Accept]"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description(
"one"))).document(new OperationBuilder("missing-request-headers",
this.snippet.getOutputDirectory()).request("http://localhost").build());
}
@Test
public void undocumentedRequestHeaderAndMissingRequestHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(endsWith("Headers with the following names were not found"
+ " in the request: [Accept]"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description(
"one"))).document(new OperationBuilder(
"undocumented-request-header-and-missing-request-header", this.snippet
.getOutputDirectory()).request("http://localhost")
.header("X-Test", "test").build());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,26 +19,20 @@ package org.springframework.restdocs.headers;
import java.io.IOException;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link RequestHeadersSnippet}.
@@ -46,13 +40,11 @@ import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
* @author Andreas Evers
* @author Andy Wilkinson
*/
public class RequestHeadersSnippetTests {
public class RequestHeadersSnippetTests extends AbstractSnippetTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public RequestHeadersSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void requestWithHeaders() throws IOException {
@@ -67,14 +59,13 @@ public class RequestHeadersSnippetTests {
.description("three"), headerWithName("Accept-Language")
.description("four"), headerWithName("Cache-Control")
.description("five"),
headerWithName("Connection").description("six")))
.document(new OperationBuilder("request-with-headers", this.snippet
.getOutputDirectory()).request("http://localhost")
.header("X-Test", "test").header("Accept", "*/*")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language", "en-US,en;q=0.5")
.header("Cache-Control", "max-age=0")
.header("Connection", "keep-alive").build());
headerWithName("Connection").description("six"))).document(operationBuilder(
"request-with-headers").request("http://localhost")
.header("X-Test", "test").header("Accept", "*/*")
.header("Accept-Encoding", "gzip, deflate")
.header("Accept-Language", "en-US,en;q=0.5")
.header("Cache-Control", "max-age=0").header("Connection", "keep-alive")
.build());
}
@Test
@@ -83,8 +74,7 @@ public class RequestHeadersSnippetTests {
.expectRequestHeaders("case-insensitive-request-headers")
.withContents(tableWithHeader("Name", "Description").row("X-Test", "one"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one"))).document(new OperationBuilder(
"case-insensitive-request-headers", this.snippet.getOutputDirectory())
"one"))).document(operationBuilder("case-insensitive-request-headers")
.request("/").header("X-test", "test").build());
}
@@ -97,37 +87,28 @@ public class RequestHeadersSnippetTests {
}
@Test
public void missingRequestHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Headers with the following names were not found"
+ " in the request: [Accept]"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description(
"one"))).document(new OperationBuilder("missing-request-headers",
this.snippet.getOutputDirectory()).request("http://localhost").build());
}
@Test
public void undocumentedRequestHeaderAndMissingRequestHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(endsWith("Headers with the following names were not found"
+ " in the request: [Accept]"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("Accept").description(
"one"))).document(new OperationBuilder(
"undocumented-request-header-and-missing-request-header", this.snippet
.getOutputDirectory()).request("http://localhost")
.header("X-Test", "test").build());
public void requestHeadersWithCustomAttributes() throws IOException {
this.snippet.expectRequestHeaders("request-headers-with-custom-attributes")
.withContents(containsString("Custom title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-headers")).willReturn(
snippetResource("request-headers-with-title"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one")), attributes(key("title").value("Custom title")))
.document(operationBuilder("request-headers-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost").header("X-Test", "test").build());
}
@Test
public void requestHeadersWithCustomDescriptorAttributes() throws IOException {
this.snippet.expectRequestHeaders("request-headers-with-custom-attributes")
.withContents(//
tableWithHeader("Name", "Description", "Foo")
.row("X-Test", "one", "alpha")
.row("Accept-Encoding", "two", "bravo")
.row("Accept", "three", "charlie"));
this.snippet.expectRequestHeaders(
"request-headers-with-custom-descriptor-attributes").withContents(//
tableWithHeader("Name", "Description", "Foo")
.row("X-Test", "one", "alpha")
.row("Accept-Encoding", "two", "bravo")
.row("Accept", "three", "charlie"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-headers")).willReturn(
snippetResource("request-headers-with-extra-column"));
@@ -137,34 +118,12 @@ public class RequestHeadersSnippetTests {
headerWithName("Accept-Encoding").description("two").attributes(
key("foo").value("bravo")),
headerWithName("Accept").description("three").attributes(
key("foo").value("charlie")))).document(new OperationBuilder(
"request-headers-with-custom-attributes", this.snippet
.getOutputDirectory())
key("foo").value("charlie")))).document(operationBuilder(
"request-headers-with-custom-descriptor-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.header("X-Test", "test").header("Accept-Encoding", "gzip, deflate")
.header("Accept", "*/*").build());
}
@Test
public void requestHeadersWithCustomAttributes() throws IOException {
this.snippet.expectRequestHeaders("request-headers-with-custom-attributes")
.withContents(startsWith(".Custom title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-headers")).willReturn(
snippetResource("request-headers-with-title"));
new RequestHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one")), attributes(key("title").value("Custom title")))
.document(new OperationBuilder("request-headers-with-custom-attributes",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost").header("X-Test", "test").build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.headers;
import java.io.IOException;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
/**
* Tests for failures when rendering {@link ResponseHeadersSnippet} due to missing or
* undocumented headers.
*
* @author Andy Wilkinson
*/
public class ResponseHeadersSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void missingResponseHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Headers with the following names were not found"
+ " in the response: [Content-Type]"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type")
.description("one"))).document(new OperationBuilder(
"missing-response-headers", this.snippet.getOutputDirectory()).response()
.build());
}
@Test
public void undocumentedResponseHeaderAndMissingResponseHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(endsWith("Headers with the following names were not found"
+ " in the response: [Content-Type]"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type")
.description("one"))).document(new OperationBuilder(
"undocumented-response-header-and-missing-response-header", this.snippet
.getOutputDirectory()).response().header("X-Test", "test")
.build());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,26 +19,20 @@ package org.springframework.restdocs.headers;
import java.io.IOException;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link ResponseHeadersSnippet}.
@@ -46,13 +40,11 @@ import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
* @author Andreas Evers
* @author Andy Wilkinson
*/
public class ResponseHeadersSnippetTests {
public class ResponseHeadersSnippetTests extends AbstractSnippetTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public ResponseHeadersSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void responseWithHeaders() throws IOException {
@@ -65,8 +57,7 @@ public class ResponseHeadersSnippetTests {
headerWithName("Content-Type").description("two"), headerWithName("Etag")
.description("three"), headerWithName("Cache-Control")
.description("five"), headerWithName("Vary").description("six")))
.document(new OperationBuilder("response-headers", this.snippet
.getOutputDirectory()).response().header("X-Test", "test")
.document(operationBuilder("response-headers").response().header("X-Test", "test")
.header("Content-Type", "application/json")
.header("Etag", "lskjadldj3ii32l2ij23")
.header("Cache-Control", "max-age=0")
@@ -79,9 +70,31 @@ public class ResponseHeadersSnippetTests {
.expectResponseHeaders("case-insensitive-response-headers")
.withContents(tableWithHeader("Name", "Description").row("X-Test", "one"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one"))).document(new OperationBuilder(
"case-insensitive-response-headers", this.snippet.getOutputDirectory())
.response().header("X-test", "test").build());
"one"))).document(operationBuilder("case-insensitive-response-headers").response()
.header("X-test", "test").build());
}
@Test
public void undocumentedResponseHeader() throws IOException {
new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one"))).document(new OperationBuilder("undocumented-response-header",
this.snippet.getOutputDirectory()).response().header("X-Test", "test")
.header("Content-Type", "*/*").build());
}
@Test
public void responseHeadersWithCustomAttributes() throws IOException {
this.snippet.expectResponseHeaders("response-headers-with-custom-attributes")
.withContents(containsString("Custom title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("response-headers")).willReturn(
snippetResource("response-headers-with-title"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one")), attributes(key("title").value("Custom title")))
.document(operationBuilder("response-headers-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.header("X-Test", "test").build());
}
@Test
@@ -101,67 +114,12 @@ public class ResponseHeadersSnippetTests {
headerWithName("Content-Type").description("two").attributes(
key("foo").value("bravo")),
headerWithName("Etag").description("three").attributes(
key("foo").value("charlie")))).document(new OperationBuilder(
"response-headers-with-custom-attributes", this.snippet
.getOutputDirectory())
key("foo").value("charlie")))).document(operationBuilder(
"response-headers-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.header("X-Test", "test").header("Content-Type", "application/json")
.header("Etag", "lskjadldj3ii32l2ij23").build());
}
@Test
public void responseHeadersWithCustomAttributes() throws IOException {
this.snippet.expectResponseHeaders("response-headers-with-custom-attributes")
.withContents(startsWith(".Custom title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("response-headers")).willReturn(
snippetResource("response-headers-with-title"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one")), attributes(key("title").value("Custom title")))
.document(new OperationBuilder("response-headers-with-custom-attributes",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.header("X-Test", "test").build());
}
@Test
public void undocumentedResponseHeader() throws IOException {
new ResponseHeadersSnippet(Arrays.asList(headerWithName("X-Test").description(
"one"))).document(new OperationBuilder("undocumented-response-header",
this.snippet.getOutputDirectory()).response().header("X-Test", "test")
.header("Content-Type", "*/*").build());
}
@Test
public void missingResponseHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Headers with the following names were not found"
+ " in the response: [Content-Type]"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type")
.description("one"))).document(new OperationBuilder(
"missing-response-headers", this.snippet.getOutputDirectory()).response()
.build());
}
@Test
public void undocumentedResponseHeaderAndMissingResponseHeader() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(endsWith("Headers with the following names were not found"
+ " in the response: [Content-Type]"));
new ResponseHeadersSnippet(Arrays.asList(headerWithName("Content-Type")
.description("one"))).document(new OperationBuilder(
"undocumented-response-header-and-missing-response-header", this.snippet
.getOutputDirectory()).response().header("X-Test", "test")
.build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,16 +18,14 @@ package org.springframework.restdocs.http;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import org.springframework.web.bind.annotation.RequestMethod;
import static org.hamcrest.CoreMatchers.containsString;
@@ -35,21 +33,20 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
/**
* Tests for {@link HttpRequestSnippet}.
*
* @author Andy Wilkinson
* @author Jonathan Pearlin
*
*/
public class HttpRequestSnippetTests {
public class HttpRequestSnippetTests extends AbstractSnippetTests {
private static final String BOUNDARY = "6o2knFse3p53ty9dmcQvWAIx1zInP11uCfbm";
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public HttpRequestSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void getRequest() throws IOException {
@@ -57,9 +54,8 @@ public class HttpRequestSnippetTests {
httpRequest(RequestMethod.GET, "/foo").header("Alpha", "a").header(
HttpHeaders.HOST, "localhost"));
new HttpRequestSnippet().document(new OperationBuilder("get-request",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
.header("Alpha", "a").build());
new HttpRequestSnippet().document(operationBuilder("get-request")
.request("http://localhost/foo").header("Alpha", "a").build());
}
@Test
@@ -68,8 +64,7 @@ public class HttpRequestSnippetTests {
httpRequest(RequestMethod.GET, "/foo?bar=baz").header(HttpHeaders.HOST,
"localhost"));
new HttpRequestSnippet().document(new OperationBuilder(
"get-request-with-query-string", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("get-request-with-query-string")
.request("http://localhost/foo?bar=baz").build());
}
@@ -81,8 +76,7 @@ public class HttpRequestSnippetTests {
.header(HttpHeaders.HOST, "localhost").content(content)
.header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length));
new HttpRequestSnippet().document(new OperationBuilder(
"post-request-with-content", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("post-request-with-content")
.request("http://localhost/foo").method("POST").content(content).build());
}
@@ -97,8 +91,7 @@ public class HttpRequestSnippetTests {
.header(HttpHeaders.CONTENT_LENGTH, contentBytes.length)
.content(japaneseContent));
new HttpRequestSnippet().document(new OperationBuilder(
"post-request-with-charset", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("post-request-with-charset")
.request("http://localhost/foo").method("POST")
.header("Content-Type", "text/plain;charset=UTF-8").content(contentBytes)
.build());
@@ -112,8 +105,7 @@ public class HttpRequestSnippetTests {
.header("Content-Type", "application/x-www-form-urlencoded")
.content("b%26r=baz&a=alpha"));
new HttpRequestSnippet().document(new OperationBuilder(
"post-request-with-parameter", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("post-request-with-parameter")
.request("http://localhost/foo").method("POST").param("b&r", "baz")
.param("a", "alpha").build());
}
@@ -126,8 +118,7 @@ public class HttpRequestSnippetTests {
.header(HttpHeaders.HOST, "localhost").content(content)
.header(HttpHeaders.CONTENT_LENGTH, content.getBytes().length));
new HttpRequestSnippet().document(new OperationBuilder(
"put-request-with-content", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("put-request-with-content")
.request("http://localhost/foo").method("PUT").content(content).build());
}
@@ -139,8 +130,7 @@ public class HttpRequestSnippetTests {
.header("Content-Type", "application/x-www-form-urlencoded")
.content("b%26r=baz&a=alpha"));
new HttpRequestSnippet().document(new OperationBuilder(
"put-request-with-parameter", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("put-request-with-parameter")
.request("http://localhost/foo").method("PUT").param("b&r", "baz")
.param("a", "alpha").build());
}
@@ -154,9 +144,8 @@ public class HttpRequestSnippetTests {
.header("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY)
.header(HttpHeaders.HOST, "localhost").content(expectedContent));
new HttpRequestSnippet().document(new OperationBuilder("multipart-post",
this.snippet.getOutputDirectory()).request("http://localhost/upload")
.method("POST")
new HttpRequestSnippet().document(operationBuilder("multipart-post")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("image", "<< data >>".getBytes()).build());
}
@@ -177,8 +166,7 @@ public class HttpRequestSnippetTests {
.header("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY)
.header(HttpHeaders.HOST, "localhost").content(expectedContent));
new HttpRequestSnippet().document(new OperationBuilder(
"multipart-post-with-parameters", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("multipart-post-with-parameters")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.param("a", "apple", "avocado").param("b", "banana")
@@ -195,8 +183,7 @@ public class HttpRequestSnippetTests {
.header("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY)
.header(HttpHeaders.HOST, "localhost").content(expectedContent));
new HttpRequestSnippet().document(new OperationBuilder(
"multipart-post-with-content-type", this.snippet.getOutputDirectory())
new HttpRequestSnippet().document(operationBuilder("multipart-post-with-content-type")
.request("http://localhost/upload").method("POST")
.header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE)
.part("image", "<< data >>".getBytes())
@@ -208,8 +195,8 @@ public class HttpRequestSnippetTests {
this.snippet.expectHttpRequest("get-request-custom-host").withContents(
httpRequest(RequestMethod.GET, "/foo").header(HttpHeaders.HOST,
"api.example.com"));
new HttpRequestSnippet().document(new OperationBuilder("get-request-custom-host",
this.snippet.getOutputDirectory()).request("http://localhost/foo")
new HttpRequestSnippet().document(operationBuilder("get-request-custom-host")
.request("http://localhost/foo")
.header(HttpHeaders.HOST, "api.example.com").build());
}
@@ -218,13 +205,10 @@ public class HttpRequestSnippetTests {
this.snippet.expectHttpRequest("request-with-snippet-attributes").withContents(
containsString("Title for the request"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("http-request"))
.willReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/http-request-with-title.snippet"));
given(resolver.resolveTemplateResource("http-request")).willReturn(
snippetResource("http-request-with-title"));
new HttpRequestSnippet(attributes(key("title").value("Title for the request")))
.document(new OperationBuilder("request-with-snippet-attributes",
this.snippet.getOutputDirectory())
.document(operationBuilder("request-with-snippet-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost/foo").build());

View File

@@ -18,24 +18,21 @@ package org.springframework.restdocs.http;
import java.io.IOException;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
/**
* Tests for {@link HttpResponseSnippet}.
@@ -43,25 +40,24 @@ import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
* @author Andy Wilkinson
* @author Jonathan Pearlin
*/
public class HttpResponseSnippetTests {
public class HttpResponseSnippetTests extends AbstractSnippetTests {
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public HttpResponseSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void basicResponse() throws IOException {
this.snippet.expectHttpResponse("basic-response").withContents(
httpResponse(HttpStatus.OK));
new HttpResponseSnippet().document(new OperationBuilder("basic-response",
this.snippet.getOutputDirectory()).build());
new HttpResponseSnippet().document(operationBuilder("basic-response").build());
}
@Test
public void nonOkResponse() throws IOException {
this.snippet.expectHttpResponse("non-ok-response").withContents(
httpResponse(HttpStatus.BAD_REQUEST));
new HttpResponseSnippet().document(new OperationBuilder("non-ok-response",
this.snippet.getOutputDirectory()).response()
new HttpResponseSnippet().document(operationBuilder("non-ok-response").response()
.status(HttpStatus.BAD_REQUEST.value()).build());
}
@@ -70,8 +66,7 @@ public class HttpResponseSnippetTests {
this.snippet.expectHttpResponse("response-with-headers").withContents(
httpResponse(HttpStatus.OK).header("Content-Type", "application/json")
.header("a", "alpha"));
new HttpResponseSnippet().document(new OperationBuilder("response-with-headers",
this.snippet.getOutputDirectory()).response()
new HttpResponseSnippet().document(operationBuilder("response-with-headers").response()
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.header("a", "alpha").build());
}
@@ -82,8 +77,8 @@ public class HttpResponseSnippetTests {
this.snippet.expectHttpResponse("response-with-content").withContents(
httpResponse(HttpStatus.OK).content(content).header(
HttpHeaders.CONTENT_LENGTH, content.getBytes().length));
new HttpResponseSnippet().document(new OperationBuilder("response-with-content",
this.snippet.getOutputDirectory()).response().content(content).build());
new HttpResponseSnippet().document(operationBuilder("response-with-content").response()
.content(content).build());
}
@Test
@@ -95,8 +90,7 @@ public class HttpResponseSnippetTests {
.header("Content-Type", "text/plain;charset=UTF-8")
.content(japaneseContent)
.header(HttpHeaders.CONTENT_LENGTH, contentBytes.length));
new HttpResponseSnippet().document(new OperationBuilder("response-with-charset",
this.snippet.getOutputDirectory()).response()
new HttpResponseSnippet().document(operationBuilder("response-with-charset").response()
.header("Content-Type", "text/plain;charset=UTF-8").content(contentBytes)
.build());
}
@@ -106,13 +100,10 @@ public class HttpResponseSnippetTests {
this.snippet.expectHttpResponse("response-with-snippet-attributes").withContents(
containsString("Title for the response"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("http-response"))
.willReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/http-response-with-title.snippet"));
given(resolver.resolveTemplateResource("http-response")).willReturn(
snippetResource("http-response-with-title"));
new HttpResponseSnippet(attributes(key("title").value("Title for the response")))
.document(new OperationBuilder("response-with-snippet-attributes",
this.snippet.getOutputDirectory()).attribute(
.document(operationBuilder("response-with-snippet-attributes").attribute(
TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.hypermedia;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
/**
* Tests for failures when rendering {@link LinksSnippet} due to missing or undocumented
* links.
*
* @author Andy Wilkinson
*/
public class LinksSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " documented: [foo]"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")),
Collections.<LinkDescriptor>emptyList()).document(new OperationBuilder(
"undocumented-link", this.snippet.getOutputDirectory()).build());
}
@Test
public void missingLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " found in the response: [foo]"));
new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo")
.description("bar"))).document(new OperationBuilder("missing-link",
this.snippet.getOutputDirectory()).build());
}
@Test
public void undocumentedLinkAndMissingLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " documented: [a]. Links with the following relations were not"
+ " found in the response: [foo]"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")),
Arrays.asList(new LinkDescriptor("foo").description("bar")))
.document(new OperationBuilder("undocumented-link-and-missing-link",
this.snippet.getOutputDirectory()).build());
}
}

View File

@@ -18,51 +18,29 @@ package org.springframework.restdocs.hypermedia;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link LinksSnippet}.
*
* @author Andy Wilkinson
*/
public class LinksSnippetTests {
public class LinksSnippetTests extends AbstractSnippetTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet();
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " documented: [foo]"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "bar")),
Collections.<LinkDescriptor>emptyList()).document(new OperationBuilder(
"undocumented-link", this.snippet.getOutputDirectory()).build());
public LinksSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
@@ -71,19 +49,8 @@ public class LinksSnippetTests {
tableWithHeader("Relation", "Description").row("b", "Link b"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
new Link("b", "bravo")), Arrays.asList(new LinkDescriptor("a").ignored(),
new LinkDescriptor("b").description("Link b")))
.document(new OperationBuilder("ignored-link", this.snippet
.getOutputDirectory()).build());
}
@Test
public void missingLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " found in the response: [foo]"));
new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo")
.description("bar"))).document(new OperationBuilder("missing-link",
this.snippet.getOutputDirectory()).build());
new LinkDescriptor("b").description("Link b"))).document(operationBuilder(
"ignored-link").build());
}
@Test
@@ -92,8 +59,7 @@ public class LinksSnippetTests {
tableWithHeader("Relation", "Description").row("foo", "bar"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("foo", "blah")),
Arrays.asList(new LinkDescriptor("foo").description("bar").optional()))
.document(new OperationBuilder("documented-optional-link", this.snippet
.getOutputDirectory()).build());
.document(operationBuilder("documented-optional-link").build());
}
@Test
@@ -101,20 +67,8 @@ public class LinksSnippetTests {
this.snippet.expectLinks("missing-optional-link").withContents(
tableWithHeader("Relation", "Description").row("foo", "bar"));
new LinksSnippet(new StubLinkExtractor(), Arrays.asList(new LinkDescriptor("foo")
.description("bar").optional())).document(new OperationBuilder(
"missing-optional-link", this.snippet.getOutputDirectory()).build());
}
@Test
public void undocumentedLinkAndMissingLink() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Links with the following relations were not"
+ " documented: [a]. Links with the following relations were not"
+ " found in the response: [foo]"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha")),
Arrays.asList(new LinkDescriptor("foo").description("bar")))
.document(new OperationBuilder("undocumented-link-and-missing-link",
this.snippet.getOutputDirectory()).build());
.description("bar").optional()))
.document(operationBuilder("missing-optional-link").build());
}
@Test
@@ -125,18 +79,32 @@ public class LinksSnippetTests {
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
new Link("b", "bravo")), Arrays.asList(
new LinkDescriptor("a").description("one"),
new LinkDescriptor("b").description("two")))
.document(new OperationBuilder("documented-links", this.snippet
.getOutputDirectory()).build());
new LinkDescriptor("b").description("two"))).document(operationBuilder(
"documented-links").build());
}
@Test
public void linksWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("links")).willReturn(
snippetResource("links-with-title"));
this.snippet.expectLinks("links-with-custom-attributes").withContents(
containsString("Title for the links"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
new Link("b", "bravo")), Arrays.asList(
new LinkDescriptor("a").description("one"),
new LinkDescriptor("b").description("two")), attributes(key("title")
.value("Title for the links"))).document(operationBuilder(
"links-with-custom-attributes").attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
@Test
public void linksWithCustomDescriptorAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("links"))
.willReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/links-with-extra-column.snippet"));
given(resolver.resolveTemplateResource("links")).willReturn(
snippetResource("links-with-extra-column"));
this.snippet.expectLinks("links-with-custom-descriptor-attributes").withContents(
tableWithHeader("Relation", "Description", "Foo")
.row("a", "one", "alpha").row("b", "two", "bravo"));
@@ -146,49 +114,10 @@ public class LinksSnippetTests {
new LinkDescriptor("a").description("one").attributes(
key("foo").value("alpha")),
new LinkDescriptor("b").description("two").attributes(
key("foo").value("bravo")))).document(new OperationBuilder(
"links-with-custom-descriptor-attributes", this.snippet
.getOutputDirectory()).attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
@Test
public void linksWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("links"))
.willReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/links-with-title.snippet"));
this.snippet.expectLinks("links-with-custom-attributes").withContents(
startsWith(".Title for the links"));
new LinksSnippet(new StubLinkExtractor().withLinks(new Link("a", "alpha"),
new Link("b", "bravo")), Arrays.asList(
new LinkDescriptor("a").description("one"),
new LinkDescriptor("b").description("two")), attributes(key("title")
.value("Title for the links"))).document(new OperationBuilder(
"links-with-custom-attributes", this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
private static class StubLinkExtractor implements LinkExtractor {
private MultiValueMap<String, Link> linksByRel = new LinkedMultiValueMap<>();
@Override
public MultiValueMap<String, Link> extractLinks(OperationResponse response)
throws IOException {
return this.linksByRel;
}
private StubLinkExtractor withLinks(Link... links) {
for (Link link : links) {
this.linksByRel.add(link.getRel(), link);
}
return this;
}
key("foo").value("bravo")))).document(operationBuilder(
"links-with-custom-descriptor-attributes").attribute(
TemplateEngine.class.getName(), new MustacheTemplateEngine(resolver))
.build());
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.hypermedia;
import java.io.IOException;
import org.springframework.restdocs.operation.OperationResponse;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Stub implementation of {@code LinkExtractor} for testing.
*
* @author Andy Wilkinson
*/
class StubLinkExtractor implements LinkExtractor {
private MultiValueMap<String, Link> linksByRel = new LinkedMultiValueMap<>();
@Override
public MultiValueMap<String, Link> extractLinks(OperationResponse response)
throws IOException {
return this.linksByRel;
}
StubLinkExtractor withLinks(Link... links) {
for (Link link : links) {
this.linksByRel.add(link.getRel(), link);
}
return this;
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.payload;
import java.io.IOException;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link RequestFieldsSnippet} that are specific to Asciidoctor.
*
* @author Andy Wilkinson
*/
public class AsciidoctorRequestFieldsSnippetTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Test
public void requestFieldsWithListDescription() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-fields")).willReturn(
snippetResource("request-fields-with-list-description"));
this.snippet.expectRequestFields("request-fields-with-list-description")
.withContents(
tableWithHeader(asciidoctor(), "Path", "Type", "Description")
//
.row("a", "String", String.format(" - one%n - two"))
.configuration("[cols=\"1,1,1a\"]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description(
Arrays.asList("one", "two"))))
.document(new OperationBuilder("request-fields-with-list-description",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost").content("{\"a\": \"foo\"}").build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/adoc/"
+ name + ".snippet");
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.payload;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
/**
* Tests for failures when rendering {@link RequestFieldsSnippet} due to missing or
* undocumented fields.
*
* @author Andy Wilkinson
*/
public class RequestFieldsSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new RequestFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-request-field", this.snippet
.getOutputDirectory()).request("http://localhost")
.content("{\"a\": 5}").build());
}
@Test
public void missingRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a.b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
.document(new OperationBuilder("missing-request-fields", this.snippet
.getOutputDirectory()).request("http://localhost").content("{}")
.build());
}
@Test
public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")
.optional())).document(new OperationBuilder(
"missing-optional-request-field-with-no-type", this.snippet
.getOutputDirectory()).request("http://localhost").content("{ }")
.build());
}
@Test
public void undocumentedRequestFieldAndMissingRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a.b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
.document(new OperationBuilder(
"undocumented-request-field-and-missing-request-field",
this.snippet.getOutputDirectory()).request("http://localhost")
.content("{ \"a\": { \"c\": 5 }}").build());
}
@Test
public void undocumentedXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new RequestFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-xml-request-field",
this.snippet.getOutputDirectory())
.request("http://localhost")
.content("<a><b>5</b></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void xmlRequestFieldWithNoType() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
.document(new OperationBuilder("missing-xml-request", this.snippet
.getOutputDirectory())
.request("http://localhost")
.content("<a>5</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
fieldWithPath("a").description("one"))).document(new OperationBuilder(
"missing-xml-request-fields", this.snippet.getOutputDirectory())
.request("http://localhost").content("<a></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlRequestFieldAndMissingXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
.document(new OperationBuilder(
"undocumented-xml-request-field-and-missing-xml-request-field",
this.snippet.getOutputDirectory())
.request("http://localhost")
.content("<a><c>5</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,43 +18,33 @@ package org.springframework.restdocs.payload;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link RequestFieldsSnippet}.
*
* @author Andy Wilkinson
*/
public class RequestFieldsSnippetTests {
public class RequestFieldsSnippetTests extends AbstractSnippetTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public RequestFieldsSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void mapRequestWithFields() throws IOException {
@@ -65,9 +55,8 @@ public class RequestFieldsSnippetTests {
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one"),
fieldWithPath("a.c").description("two"),
fieldWithPath("a").description("three"))).document(new OperationBuilder(
"map-request-with-fields", this.snippet.getOutputDirectory())
.request("http://localhost")
fieldWithPath("a").description("three"))).document(operationBuilder(
"map-request-with-fields").request("http://localhost")
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
}
@@ -80,24 +69,11 @@ public class RequestFieldsSnippetTests {
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("[]a.b").description("one"),
fieldWithPath("[]a.c").description("two"), fieldWithPath("[]a")
.description("three"))).document(new OperationBuilder(
"array-request-with-fields", this.snippet.getOutputDirectory())
.request("http://localhost")
.description("three"))).document(operationBuilder(
"array-request-with-fields").request("http://localhost")
.content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]").build());
}
@Test
public void undocumentedRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new RequestFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-request-field", this.snippet
.getOutputDirectory()).request("http://localhost")
.content("{\"a\": 5}").build());
}
@Test
public void ignoredRequestField() throws IOException {
this.snippet.expectRequestFields("ignored-request-field").withContents(
@@ -105,48 +81,25 @@ public class RequestFieldsSnippetTests {
"Field b"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(),
fieldWithPath("b").description("Field b")))
.document(new OperationBuilder("ignored-request-field", this.snippet
.getOutputDirectory()).request("http://localhost")
.content("{\"a\": 5, \"b\": 4}").build());
fieldWithPath("b").description("Field b"))).document(operationBuilder(
"ignored-request-field").request("http://localhost")
.content("{\"a\": 5, \"b\": 4}").build());
}
@Test
public void missingRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a.b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
.document(new OperationBuilder("missing-request-fields", this.snippet
.getOutputDirectory()).request("http://localhost").content("{}")
.build());
}
public void requestFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-fields")).willReturn(
snippetResource("request-fields-with-title"));
this.snippet.expectRequestFields("request-fields-with-custom-attributes")
.withContents(containsString("Custom title"));
@Test
public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")
.optional())).document(new OperationBuilder(
"missing-optional-request-field-with-no-type", this.snippet
.getOutputDirectory()).request("http://localhost").content("{ }")
.build());
}
@Test
public void undocumentedRequestFieldAndMissingRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a.b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a.b").description("one")))
.document(new OperationBuilder(
"undocumented-request-field-and-missing-request-field",
this.snippet.getOutputDirectory()).request("http://localhost")
.content("{ \"a\": { \"c\": 5 }}").build());
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")),
attributes(key("title").value("Custom title"))).document(operationBuilder(
"request-fields-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.content("{\"a\": \"foo\"}").build());
}
@Test
@@ -167,52 +120,13 @@ public class RequestFieldsSnippetTests {
fieldWithPath("a.c").description("two").attributes(
key("foo").value("bravo")),
fieldWithPath("a").description("three").attributes(
key("foo").value("charlie")))).document(new OperationBuilder(
"request-fields-with-custom-descriptor-attributes", this.snippet
.getOutputDirectory())
key("foo").value("charlie")))).document(operationBuilder(
"request-fields-with-custom-descriptor-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
}
@Test
public void requestFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-fields")).willReturn(
snippetResource("request-fields-with-title"));
this.snippet.expectRequestFields("request-fields-with-custom-attributes")
.withContents(startsWith(".Custom title"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")),
attributes(key("title").value("Custom title")))
.document(new OperationBuilder("request-fields-with-custom-attributes",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost").content("{\"a\": \"foo\"}").build());
}
@Test
public void requestFieldsWithListDescription() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-fields")).willReturn(
snippetResource("request-fields-with-list-description"));
this.snippet.expectRequestFields("request-fields-with-list-description")
.withContents(
tableWithHeader("Path", "Type", "Description")
//
.row("a", "String", String.format(" - one%n - two"))
.configuration("[cols=\"1,1,1a\"]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description(
Arrays.asList("one", "two"))))
.document(new OperationBuilder("request-fields-with-list-description",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))
.request("http://localhost").content("{\"a\": \"foo\"}").build());
}
@Test
public void xmlRequestFields() throws IOException {
this.snippet.expectRequestFields("xml-request").withContents(
@@ -221,78 +135,11 @@ public class RequestFieldsSnippetTests {
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")
.type("b"), fieldWithPath("a/c").description("two").type("c"),
fieldWithPath("a").description("three").type("a")))
.document(new OperationBuilder("xml-request", this.snippet
.getOutputDirectory())
.request("http://localhost")
.content("<a><b>5</b><c>charlie</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new RequestFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-xml-request-field",
this.snippet.getOutputDirectory())
.request("http://localhost")
.content("<a><b>5</b></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void xmlRequestFieldWithNoType() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
.document(new OperationBuilder("missing-xml-request", this.snippet
.getOutputDirectory())
.request("http://localhost")
.content("<a>5</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
fieldWithPath("a").description("one"))).document(new OperationBuilder(
"missing-xml-request-fields", this.snippet.getOutputDirectory())
.request("http://localhost").content("<a></a>")
fieldWithPath("a").description("three").type("a"))).document(operationBuilder(
"xml-request").request("http://localhost")
.content("<a><b>5</b><c>charlie</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlRequestFieldAndMissingXmlRequestField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new RequestFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
.document(new OperationBuilder(
"undocumented-xml-request-field-and-missing-xml-request-field",
this.snippet.getOutputDirectory())
.request("http://localhost")
.content("<a><c>5</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.payload;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
/**
* Tests for failures when rendering {@link ResponseFieldsSnippet} due to missing or
* undocumented fields.
*
* @author Andy Wilkinson
*/
public class ResponseFieldsSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedXmlResponseField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new ResponseFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-xml-response-field",
this.snippet.getOutputDirectory())
.response()
.content("<a><b>5</b></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlAttribute() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/@id]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")
.type("b"), fieldWithPath("a/@id").description("two").type("c")))
.document(new OperationBuilder("missing-xml-attribute", this.snippet
.getOutputDirectory())
.response()
.content("<a>foo</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void documentedXmlAttributesAreRemoved() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo(String
.format("The following parts of the payload were not documented:"
+ "%n<a>bar</a>%n")));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one")
.type("a"))).document(new OperationBuilder(
"documented-attribute-is-removed", this.snippet.getOutputDirectory())
.response().content("<a id=\"foo\">bar</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void xmlResponseFieldWithNoType() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
.document(new OperationBuilder("xml-response-no-field-type", this.snippet
.getOutputDirectory())
.response()
.content("<a>5</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlResponseField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
fieldWithPath("a").description("one"))).document(new OperationBuilder(
"missing-xml-response-field", this.snippet.getOutputDirectory())
.response().content("<a></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlResponseFieldAndMissingXmlResponseField()
throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
.document(new OperationBuilder(
"undocumented-xml-request-field-and-missing-xml-request-field",
this.snippet.getOutputDirectory())
.response()
.content("<a><c>5</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,43 +18,33 @@ package org.springframework.restdocs.payload;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.endsWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link ResponseFieldsSnippet}.
*
* @author Andy Wilkinson
*/
public class ResponseFieldsSnippetTests {
public class ResponseFieldsSnippetTests extends AbstractSnippetTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Rule
public final ExpectedSnippet snippet = new ExpectedSnippet();
public ResponseFieldsSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
public void mapResponseWithFields() throws IOException {
@@ -69,14 +59,12 @@ public class ResponseFieldsSnippetTests {
.description("three"),
fieldWithPath("assets[]").description("four"),
fieldWithPath("assets[].id").description("five"),
fieldWithPath("assets[].name").description("six")))
.document(new OperationBuilder("map-response-with-fields", this.snippet
.getOutputDirectory())
.response()
.content(
"{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":"
+ " [{\"id\":356,\"name\": \"sample\"}]}")
.build());
fieldWithPath("assets[].name").description("six"))).document(operationBuilder(
"map-response-with-fields")
.response()
.content(
"{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":"
+ " [{\"id\":356,\"name\": \"sample\"}]}").build());
}
@Test
@@ -88,8 +76,7 @@ public class ResponseFieldsSnippetTests {
new ResponseFieldsSnippet(Arrays.asList(
fieldWithPath("[]a.b").description("one"), fieldWithPath("[]a.c")
.description("two"), fieldWithPath("[]a").description("three")))
.document(new OperationBuilder("array-response-with-fields", this.snippet
.getOutputDirectory()).response()
.document(operationBuilder("array-response-with-fields").response()
.content("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]")
.build());
}
@@ -101,8 +88,7 @@ public class ResponseFieldsSnippetTests {
tableWithHeader("Path", "Type", "Description").row("[]",
"String", "one"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("[]").description("one")))
.document(new OperationBuilder("array-response", this.snippet
.getOutputDirectory()).response()
.document(operationBuilder("array-response").response()
.content("[\"a\", \"b\", \"c\"]").build());
}
@@ -113,10 +99,25 @@ public class ResponseFieldsSnippetTests {
"Field b"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").ignored(),
fieldWithPath("b").description("Field b")))
.document(new OperationBuilder("ignored-response-field", this.snippet
.getOutputDirectory()).response().content("{\"a\": 5, \"b\": 4}")
.build());
fieldWithPath("b").description("Field b"))).document(operationBuilder(
"ignored-response-field").response().content("{\"a\": 5, \"b\": 4}")
.build());
}
@Test
public void responseFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("response-fields")).willReturn(
snippetResource("response-fields-with-title"));
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
.withContents(containsString("Custom title"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")),
attributes(key("title").value("Custom title"))).document(operationBuilder(
"response-fields-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.content("{\"a\": \"foo\"}").build());
}
@Test
@@ -137,31 +138,13 @@ public class ResponseFieldsSnippetTests {
fieldWithPath("a.c").description("two").attributes(
key("foo").value("bravo")),
fieldWithPath("a").description("three").attributes(
key("foo").value("charlie")))).document(new OperationBuilder(
"response-fields-with-custom-attributes", this.snippet
.getOutputDirectory())
key("foo").value("charlie")))).document(operationBuilder(
"response-fields-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.content("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}").build());
}
@Test
public void responseFieldsWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("response-fields")).willReturn(
snippetResource("response-fields-with-title"));
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
.withContents(startsWith(".Custom title"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")),
attributes(key("title").value("Custom title")))
.document(new OperationBuilder("response-fields-with-custom-attributes",
this.snippet.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).response()
.content("{\"a\": \"foo\"}").build());
}
@Test
public void xmlResponseFields() throws IOException {
this.snippet.expectResponseFields("xml-response").withContents(
@@ -169,28 +152,10 @@ public class ResponseFieldsSnippetTests {
.row("a/c", "c", "two").row("a", "a", "three"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")
.type("b"), fieldWithPath("a/c").description("two").type("c"),
fieldWithPath("a").description("three").type("a")))
.document(new OperationBuilder("xml-response", this.snippet
.getOutputDirectory())
.response()
.content("<a><b>5</b><c>charlie</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlResponseField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
new ResponseFieldsSnippet(Collections.<FieldDescriptor>emptyList())
.document(new OperationBuilder("undocumented-xml-response-field",
this.snippet.getOutputDirectory())
.response()
.content("<a><b>5</b></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
fieldWithPath("a").description("three").type("a"))).document(operationBuilder(
"xml-response").response().content("<a><b>5</b><c>charlie</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
@@ -200,30 +165,13 @@ public class ResponseFieldsSnippetTests {
"a/@id", "c", "two"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")
.type("b"), fieldWithPath("a/@id").description("two").type("c")))
.document(new OperationBuilder("xml-attribute", this.snippet
.getOutputDirectory())
.document(operationBuilder("xml-attribute")
.response()
.content("<a id=\"1\">foo</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlAttribute() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/@id]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")
.type("b"), fieldWithPath("a/@id").description("two").type("c")))
.document(new OperationBuilder("missing-xml-attribute", this.snippet
.getOutputDirectory())
.response()
.content("<a>foo</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingOptionalXmlAttribute() throws IOException {
this.snippet.expectResponseFields("missing-optional-xml-attribute").withContents(
@@ -231,8 +179,7 @@ public class ResponseFieldsSnippetTests {
"a/@id", "c", "two"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")
.type("b"), fieldWithPath("a/@id").description("two").type("c")
.optional())).document(new OperationBuilder(
"missing-optional-xml-attribute", this.snippet.getOutputDirectory())
.optional())).document(operationBuilder("missing-optional-xml-attribute")
.response().content("<a>foo</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
@@ -243,76 +190,10 @@ public class ResponseFieldsSnippetTests {
this.snippet.expectResponseFields("undocumented-attribute").withContents(
tableWithHeader("Path", "Type", "Description").row("a", "a", "one"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")
.type("a"))).document(new OperationBuilder("undocumented-attribute",
this.snippet.getOutputDirectory()).response()
.type("a"))).document(operationBuilder("undocumented-attribute").response()
.content("<a id=\"foo\">bar</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void documentedXmlAttributesAreRemoved() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo(String
.format("The following parts of the payload were not documented:"
+ "%n<a>bar</a>%n")));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/@id").description("one")
.type("a"))).document(new OperationBuilder(
"documented-attribute-is-removed", this.snippet.getOutputDirectory())
.response().content("<a id=\"foo\">bar</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void xmlResponseFieldWithNoType() throws IOException {
this.thrown.expect(FieldTypeRequiredException.class);
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a").description("one")))
.document(new OperationBuilder("xml-response-no-field-type", this.snippet
.getOutputDirectory())
.response()
.content("<a>5</a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void missingXmlResponseField() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one"),
fieldWithPath("a").description("one"))).document(new OperationBuilder(
"missing-xml-response-field", this.snippet.getOutputDirectory())
.response().content("<a></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
@Test
public void undocumentedXmlResponseFieldAndMissingXmlResponseField()
throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(startsWith("The following parts of the payload were not"
+ " documented:"));
this.thrown
.expectMessage(endsWith("Fields with the following paths were not found"
+ " in the payload: [a/b]"));
new ResponseFieldsSnippet(Arrays.asList(fieldWithPath("a/b").description("one")))
.document(new OperationBuilder(
"undocumented-xml-request-field-and-missing-xml-request-field",
this.snippet.getOutputDirectory())
.response()
.content("<a><c>5</c></a>")
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
.build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
/**
* Tests for failures when rendering {@link PathParametersSnippet} due to missing or
* undocumented path parameters.
*
* @author Andy Wilkinson
*/
public class PathParametersSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedPathParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [a]"));
new PathParametersSnippet(Collections.<ParameterDescriptor>emptyList())
.document(new OperationBuilder("undocumented-path-parameter",
this.snippet.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/").build());
}
@Test
public void missingPathParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not found in the request: [a]"));
new PathParametersSnippet(
Arrays.asList(parameterWithName("a").description("one")))
.document(new OperationBuilder("missing-path-parameter", this.snippet
.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/").build());
}
@Test
public void undocumentedAndMissingPathParameters() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [b]. Path parameters with the following"
+ " names were not found in the request: [a]"));
new PathParametersSnippet(
Arrays.asList(parameterWithName("a").description("one")))
.document(new OperationBuilder(
"undocumented-and-missing-path-parameters", this.snippet
.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{b}").build());
}
}

View File

@@ -18,78 +18,30 @@ package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithTitleAndHeader;
/**
* Tests for {@link PathParametersSnippet}.
*
* @author Andy Wilkinson
*
*/
public class PathParametersSnippetTests {
public class PathParametersSnippetTests extends AbstractSnippetTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet();
@Test
public void undocumentedPathParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [a]"));
new PathParametersSnippet(Collections.<ParameterDescriptor>emptyList())
.document(new OperationBuilder("undocumented-path-parameter",
this.snippet.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/").build());
}
@Test
public void missingPathParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not found in the request: [a]"));
new PathParametersSnippet(
Arrays.asList(parameterWithName("a").description("one")))
.document(new OperationBuilder("missing-path-parameter", this.snippet
.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/").build());
}
@Test
public void undocumentedAndMissingPathParameters() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [b]. Path parameters with the following"
+ " names were not found in the request: [a]"));
new PathParametersSnippet(
Arrays.asList(parameterWithName("a").description("one")))
.document(new OperationBuilder(
"undocumented-and-missing-path-parameters", this.snippet
.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{b}").build());
public PathParametersSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
@@ -99,9 +51,9 @@ public class PathParametersSnippetTests {
"one").row("b", "two"));
new PathParametersSnippet(Arrays.asList(
parameterWithName("a").description("one"), parameterWithName("b")
.description("two"))).document(new OperationBuilder(
"path-parameters", this.snippet.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/{b}").build());
.description("two"))).document(operationBuilder("path-parameters")
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
.build());
}
@Test
@@ -110,10 +62,9 @@ public class PathParametersSnippetTests {
tableWithTitleAndHeader("/{a}/{b}", "Parameter", "Description").row("b",
"two"));
new PathParametersSnippet(Arrays.asList(parameterWithName("a").ignored(),
parameterWithName("b").description("two")))
.document(new OperationBuilder("ignored-path-parameter", this.snippet
.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/{b}").build());
parameterWithName("b").description("two"))).document(operationBuilder(
"ignored-path-parameter").attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/{b}").build());
}
@Test
@@ -124,11 +75,28 @@ public class PathParametersSnippetTests {
.row("a", "one").row("b", "two"));
new PathParametersSnippet(Arrays.asList(
parameterWithName("a").description("one"), parameterWithName("b")
.description("two")))
.document(new OperationBuilder("path-parameters-with-query-string",
this.snippet.getOutputDirectory()).attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/{b}?foo=bar")
.build());
.description("two"))).document(operationBuilder(
"path-parameters-with-query-string").attribute(
"org.springframework.restdocs.urlTemplate", "/{a}/{b}?foo=bar").build());
}
@Test
public void pathParametersWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("path-parameters")).willReturn(
snippetResource("path-parameters-with-title"));
this.snippet.expectPathParameters("path-parameters-with-custom-attributes")
.withContents(containsString("The title"));
new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one")
.attributes(key("foo").value("alpha")), parameterWithName("b")
.description("two").attributes(key("foo").value("bravo"))),
attributes(key("title").value("The title"))).document(operationBuilder(
"path-parameters-with-custom-attributes")
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
@Test
@@ -144,37 +112,10 @@ public class PathParametersSnippetTests {
new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one")
.attributes(key("foo").value("alpha")), parameterWithName("b")
.description("two").attributes(key("foo").value("bravo"))))
.document(new OperationBuilder(
"path-parameters-with-custom-descriptor-attributes", this.snippet
.getOutputDirectory())
.document(operationBuilder("path-parameters-with-custom-descriptor-attributes")
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
@Test
public void pathParametersWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("path-parameters")).willReturn(
snippetResource("path-parameters-with-title"));
this.snippet.expectPathParameters("path-parameters-with-custom-attributes")
.withContents(startsWith(".The title"));
new PathParametersSnippet(Arrays.asList(parameterWithName("a").description("one")
.attributes(key("foo").value("alpha")), parameterWithName("b")
.description("two").attributes(key("foo").value("bravo"))),
attributes(key("title").value("The title")))
.document(new OperationBuilder("path-parameters-with-custom-attributes",
this.snippet.getOutputDirectory())
.attribute("org.springframework.restdocs.urlTemplate", "/{a}/{b}")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
/**
* Tests for failures when rendering {@link RequestParametersSnippet} due to missing or
* undocumented request parameters.
*
* @author Andy Wilkinson
*/
public class RequestParametersSnippetFailureTests {
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet(SnippetFormats.asciidoctor());
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void undocumentedParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not documented: [a]"));
new RequestParametersSnippet(Collections.<ParameterDescriptor>emptyList())
.document(new OperationBuilder("undocumented-parameter", this.snippet
.getOutputDirectory()).request("http://localhost")
.param("a", "alpha").build());
}
@Test
public void missingParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not found in the request: [a]"));
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
"one"))).document(new OperationBuilder("missing-parameter", this.snippet
.getOutputDirectory()).request("http://localhost").build());
}
@Test
public void undocumentedAndMissingParameters() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not documented: [b]. Request parameters with the following"
+ " names were not found in the request: [a]"));
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
"one"))).document(new OperationBuilder(
"undocumented-and-missing-parameters", this.snippet.getOutputDirectory())
.request("http://localhost").param("b", "bravo").build());
}
}

View File

@@ -18,75 +18,30 @@ package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.core.io.FileSystemResource;
import org.springframework.restdocs.snippet.SnippetException;
import org.springframework.restdocs.AbstractSnippetTests;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.templates.TemplateEngine;
import org.springframework.restdocs.templates.TemplateResourceResolver;
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
import org.springframework.restdocs.test.ExpectedSnippet;
import org.springframework.restdocs.test.OperationBuilder;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
/**
* Tests for {@link RequestParametersSnippet}.
*
* @author Andy Wilkinson
*/
public class RequestParametersSnippetTests {
public class RequestParametersSnippetTests extends AbstractSnippetTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Rule
public ExpectedSnippet snippet = new ExpectedSnippet();
@Test
public void undocumentedParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not documented: [a]"));
new RequestParametersSnippet(Collections.<ParameterDescriptor>emptyList())
.document(new OperationBuilder("undocumented-parameter", this.snippet
.getOutputDirectory()).request("http://localhost")
.param("a", "alpha").build());
}
@Test
public void missingParameter() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not found in the request: [a]"));
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
"one"))).document(new OperationBuilder("missing-parameter", this.snippet
.getOutputDirectory()).request("http://localhost").build());
}
@Test
public void undocumentedAndMissingParameters() throws IOException {
this.thrown.expect(SnippetException.class);
this.thrown
.expectMessage(equalTo("Request parameters with the following names were"
+ " not documented: [b]. Request parameters with the following"
+ " names were not found in the request: [a]"));
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").description(
"one"))).document(new OperationBuilder(
"undocumented-and-missing-parameters", this.snippet.getOutputDirectory())
.request("http://localhost").param("b", "bravo").build());
public RequestParametersSnippetTests(String name, SnippetFormat snippetFormat) {
super(name, snippetFormat);
}
@Test
@@ -96,8 +51,7 @@ public class RequestParametersSnippetTests {
"two"));
new RequestParametersSnippet(Arrays.asList(
parameterWithName("a").description("one"), parameterWithName("b")
.description("two"))).document(new OperationBuilder(
"request-parameters", this.snippet.getOutputDirectory())
.description("two"))).document(operationBuilder("request-parameters")
.request("http://localhost").param("a", "bravo").param("b", "bravo")
.build());
}
@@ -107,10 +61,29 @@ public class RequestParametersSnippetTests {
this.snippet.expectRequestParameters("ignored-request-parameter").withContents(
tableWithHeader("Parameter", "Description").row("b", "two"));
new RequestParametersSnippet(Arrays.asList(parameterWithName("a").ignored(),
parameterWithName("b").description("two")))
.document(new OperationBuilder("ignored-request-parameter", this.snippet
.getOutputDirectory()).request("http://localhost")
.param("a", "bravo").param("b", "bravo").build());
parameterWithName("b").description("two"))).document(operationBuilder(
"ignored-request-parameter").request("http://localhost")
.param("a", "bravo").param("b", "bravo").build());
}
@Test
public void requestParametersWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-parameters")).willReturn(
snippetResource("request-parameters-with-title"));
this.snippet.expectRequestParameters("request-parameters-with-custom-attributes")
.withContents(containsString("The title"));
new RequestParametersSnippet(Arrays.asList(
parameterWithName("a").description("one").attributes(
key("foo").value("alpha")),
parameterWithName("b").description("two").attributes(
key("foo").value("bravo"))), attributes(key("title").value(
"The title"))).document(operationBuilder(
"request-parameters-with-custom-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.param("a", "alpha").param("b", "bravo").build());
}
@Test
@@ -127,38 +100,11 @@ public class RequestParametersSnippetTests {
parameterWithName("a").description("one").attributes(
key("foo").value("alpha")),
parameterWithName("b").description("two").attributes(
key("foo").value("bravo")))).document(new OperationBuilder(
"request-parameters-with-custom-descriptor-attributes", this.snippet
.getOutputDirectory())
key("foo").value("bravo")))).document(operationBuilder(
"request-parameters-with-custom-descriptor-attributes")
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.param("a", "alpha").param("b", "bravo").build());
}
@Test
public void requestParametersWithCustomAttributes() throws IOException {
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
given(resolver.resolveTemplateResource("request-parameters")).willReturn(
snippetResource("request-parameters-with-title"));
this.snippet.expectRequestParameters("request-parameters-with-custom-attributes")
.withContents(startsWith(".The title"));
new RequestParametersSnippet(Arrays.asList(
parameterWithName("a").description("one").attributes(
key("foo").value("alpha")),
parameterWithName("b").description("two").attributes(
key("foo").value("bravo"))), attributes(key("title").value(
"The title"))).document(new OperationBuilder(
"request-parameters-with-custom-attributes", this.snippet
.getOutputDirectory())
.attribute(TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver)).request("http://localhost")
.param("a", "alpha").param("b", "bravo").build());
}
private FileSystemResource snippetResource(String name) {
return new FileSystemResource("src/test/resources/custom-snippet-templates/"
+ name + ".snippet");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor;
/**
* Tests for {@link StandardWriterResolver}.
@@ -37,7 +38,7 @@ public class StandardWriterResolverTests {
private final PlaceholderResolver placeholderResolver = mock(PlaceholderResolver.class);
private final StandardWriterResolver resolver = new StandardWriterResolver(
this.placeholderResolver);
this.placeholderResolver, "UTF-8", asciidoctor());
@Test
public void noConfiguredOutputDirectoryAndRelativeInput() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import org.springframework.core.io.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor;
/**
* Tests for {@link TemplateResourceResolver}.
@@ -40,7 +41,8 @@ public class StandardTemplateResourceResolverTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver();
private final TemplateResourceResolver resolver = new StandardTemplateResourceResolver(
asciidoctor());
private final TestClassLoader classLoader = new TestClassLoader();
@@ -66,7 +68,7 @@ public class StandardTemplateResourceResolverTests {
@Test
public void fallsBackToDefaultSnippet() throws Exception {
this.classLoader.addResource(
"org/springframework/restdocs/templates/default-test.snippet", getClass()
"org/springframework/restdocs/templates/adoc/test.snippet", getClass()
.getResource("test.snippet"));
Resource snippet = doWithThreadContextClassLoader(this.classLoader,
new Callable<Resource>() {

View File

@@ -23,6 +23,7 @@ import org.hamcrest.Matcher;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.snippet.TemplatedSnippet;
import org.springframework.restdocs.test.SnippetMatchers.SnippetMatcher;
@@ -38,14 +39,21 @@ import static org.junit.Assert.assertThat;
*/
public class ExpectedSnippet implements TestRule {
private final SnippetFormat snippetFormat;
private final SnippetMatcher snippet;
private String expectedName;
private String expectedType;
private SnippetMatcher snippet = SnippetMatchers.snippet();
private File outputDirectory;
public ExpectedSnippet(SnippetFormat snippetFormat) {
this.snippetFormat = snippetFormat;
this.snippet = SnippetMatchers.snippet(snippetFormat);
}
@Override
public Statement apply(final Statement base, Description description) {
this.outputDirectory = new File("build/"
@@ -56,7 +64,8 @@ public class ExpectedSnippet implements TestRule {
private void verifySnippet() throws IOException {
if (this.outputDirectory != null && this.expectedName != null) {
File snippetDir = new File(this.outputDirectory, this.expectedName);
File snippetFile = new File(snippetDir, this.expectedType + ".adoc");
File snippetFile = new File(snippetDir, this.expectedType + "."
+ this.snippetFormat.getFileExtension());
assertThat(snippetFile, is(this.snippet));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2015 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,6 +37,8 @@ import org.springframework.restdocs.operation.OperationResponseFactory;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.StandardOperation;
import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolver;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.restdocs.snippet.StandardWriterResolver;
import org.springframework.restdocs.snippet.WriterResolver;
import org.springframework.restdocs.templates.StandardTemplateResourceResolver;
@@ -58,11 +60,18 @@ public class OperationBuilder {
private final File outputDirectory;
private final SnippetFormat snippetFormat;
private OperationRequestBuilder requestBuilder;
public OperationBuilder(String name, File outputDirectory) {
this(name, outputDirectory, SnippetFormats.asciidoctor());
}
public OperationBuilder(String name, File outputDirectory, SnippetFormat snippetFormat) {
this.name = name;
this.outputDirectory = outputDirectory;
this.snippetFormat = snippetFormat;
}
public OperationRequestBuilder request(String uri) {
@@ -82,13 +91,15 @@ public class OperationBuilder {
public Operation build() {
if (this.attributes.get(TemplateEngine.class.getName()) == null) {
this.attributes.put(TemplateEngine.class.getName(),
new MustacheTemplateEngine(new StandardTemplateResourceResolver()));
new MustacheTemplateEngine(new StandardTemplateResourceResolver(
this.snippetFormat)));
}
RestDocumentationContext context = new RestDocumentationContext(null, null,
this.outputDirectory);
this.attributes.put(RestDocumentationContext.class.getName(), context);
this.attributes.put(WriterResolver.class.getName(), new StandardWriterResolver(
new RestDocumentationContextPlaceholderResolver(context)));
new RestDocumentationContextPlaceholderResolver(context), "UTF-8",
this.snippetFormat));
return new StandardOperation(this.name,
(this.requestBuilder == null ? new OperationRequestBuilder(
"http://localhost/").buildRequest() : this.requestBuilder

View File

@@ -30,6 +30,8 @@ import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.springframework.http.HttpStatus;
import org.springframework.restdocs.snippet.SnippetFormat;
import org.springframework.restdocs.snippet.SnippetFormats;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMethod;
@@ -45,37 +47,62 @@ public final class SnippetMatchers {
}
public static SnippetMatcher snippet() {
return new SnippetMatcher();
public static SnippetMatcher snippet(SnippetFormat snippetFormat) {
return new SnippetMatcher(snippetFormat);
}
public static AsciidoctorTableMatcher tableWithTitleAndHeader(String title,
String... headers) {
return new AsciidoctorTableMatcher(title, headers);
public static TableMatcher<?> tableWithHeader(SnippetFormat format, String... headers) {
if ("adoc".equals(format.getFileExtension())) {
return new AsciidoctorTableMatcher(null, headers);
}
return new MarkdownTableMatcher(null, headers);
}
public static AsciidoctorTableMatcher tableWithHeader(String... headers) {
return new AsciidoctorTableMatcher(null, headers);
public static TableMatcher<?> tableWithTitleAndHeader(SnippetFormat format,
String title, String... headers) {
if ("adoc".equals(format.getFileExtension())) {
return new AsciidoctorTableMatcher(title, headers);
}
return new MarkdownTableMatcher(title, headers);
}
public static HttpRequestMatcher httpRequest(RequestMethod method, String uri) {
return new HttpRequestMatcher(method, uri);
public static HttpRequestMatcher httpRequest(SnippetFormat format,
RequestMethod requestMethod, String uri) {
if ("adoc".equals(format.getFileExtension())) {
return new HttpRequestMatcher(requestMethod, uri,
new AsciidoctorCodeBlockMatcher<>("http"), 3);
}
return new HttpRequestMatcher(requestMethod, uri, new MarkdownCodeBlockMatcher<>(
"http"), 2);
}
public static HttpResponseMatcher httpResponse(HttpStatus status) {
return new HttpResponseMatcher(status);
public static HttpResponseMatcher httpResponse(SnippetFormat format, HttpStatus status) {
if ("adoc".equals(format.getFileExtension())) {
return new HttpResponseMatcher(status, new AsciidoctorCodeBlockMatcher<>(
"http"), 3);
}
return new HttpResponseMatcher(status, new MarkdownCodeBlockMatcher<>("http"), 2);
}
@SuppressWarnings({ "rawtypes" })
public static AsciidoctorCodeBlockMatcher<?> codeBlock(String language) {
return new AsciidoctorCodeBlockMatcher(language);
public static CodeBlockMatcher<?> codeBlock(SnippetFormat format, String language) {
if ("adoc".equals(format.getFileExtension())) {
return new AsciidoctorCodeBlockMatcher(language);
}
return new MarkdownCodeBlockMatcher(language);
}
private static abstract class AbstractSnippetContentMatcher extends
BaseMatcher<String> {
private final SnippetFormat snippetFormat;
private List<String> lines = new ArrayList<>();
protected AbstractSnippetContentMatcher(SnippetFormat snippetFormat) {
this.snippetFormat = snippetFormat;
}
protected void addLine(String line) {
this.lines.add(line);
}
@@ -94,7 +121,7 @@ public final class SnippetMatchers {
@Override
public void describeTo(Description description) {
description.appendText("Asciidoctor snippet");
description.appendText(this.snippetFormat.getFileExtension() + " snippet");
description.appendText(getLinesAsString());
}
@@ -123,17 +150,15 @@ public final class SnippetMatchers {
}
/**
* A {@link Matcher} for an Asciidoctor code block.
* Base class for code block matchers.
*
* @param <T> The type of the matcher
*/
public static class AsciidoctorCodeBlockMatcher<T extends AsciidoctorCodeBlockMatcher<T>>
extends AbstractSnippetContentMatcher {
public static class CodeBlockMatcher<T extends CodeBlockMatcher<T>> extends
AbstractSnippetContentMatcher {
protected AsciidoctorCodeBlockMatcher(String language) {
this.addLine("[source," + language + "]");
this.addLine("----");
this.addLine("----");
protected CodeBlockMatcher(SnippetFormat snippetFormat) {
super(snippetFormat);
}
@SuppressWarnings("unchecked")
@@ -144,32 +169,84 @@ public final class SnippetMatchers {
}
/**
* A {@link Matcher} for an Asciidoctor code block.
*
* @param <T> The type of the matcher
*/
public static class AsciidoctorCodeBlockMatcher<T extends AsciidoctorCodeBlockMatcher<T>>
extends CodeBlockMatcher<T> {
protected AsciidoctorCodeBlockMatcher(String language) {
super(SnippetFormats.asciidoctor());
this.addLine("[source," + language + "]");
this.addLine("----");
this.addLine("----");
}
}
/**
* A {@link Matcher} for a Markdown code block.
*
* @param <T> The type of the matcher
*/
public static class MarkdownCodeBlockMatcher<T extends MarkdownCodeBlockMatcher<T>>
extends CodeBlockMatcher<T> {
protected MarkdownCodeBlockMatcher(String language) {
super(SnippetFormats.markdown());
this.addLine("```" + language);
this.addLine("```");
}
}
/**
* A {@link Matcher} for an HTTP request or response.
*
* @param <T> The type of the matcher
*/
public static abstract class HttpMatcher<T extends HttpMatcher<T>> extends
AsciidoctorCodeBlockMatcher<HttpMatcher<T>> {
BaseMatcher<String> {
private int headerOffset = 3;
private final CodeBlockMatcher<?> delegate;
protected HttpMatcher() {
super("http");
private int headerOffset;
protected HttpMatcher(CodeBlockMatcher<?> delegate, int headerOffset) {
this.delegate = delegate;
this.headerOffset = headerOffset;
}
@SuppressWarnings("unchecked")
public T header(String name, String value) {
this.addLine(this.headerOffset++, name + ": " + value);
this.delegate.addLine(this.headerOffset++, name + ": " + value);
return (T) this;
}
@SuppressWarnings("unchecked")
public T header(String name, long value) {
this.addLine(this.headerOffset++, name + ": " + value);
this.delegate.addLine(this.headerOffset++, name + ": " + value);
return (T) this;
}
@SuppressWarnings("unchecked")
public T content(String content) {
this.delegate.addLine(-1, content);
return (T) this;
}
@Override
public boolean matches(Object item) {
return this.delegate.matches(item);
}
@Override
public void describeTo(Description description) {
this.delegate.describeTo(description);
}
}
/**
@@ -178,7 +255,9 @@ public final class SnippetMatchers {
public static final class HttpResponseMatcher extends
HttpMatcher<HttpResponseMatcher> {
private HttpResponseMatcher(HttpStatus status) {
private HttpResponseMatcher(HttpStatus status, CodeBlockMatcher<?> delegate,
int headerOffset) {
super(delegate, headerOffset);
this.content("HTTP/1.1 " + status.value() + " " + status.getReasonPhrase());
this.content("");
}
@@ -190,20 +269,41 @@ public final class SnippetMatchers {
*/
public static final class HttpRequestMatcher extends HttpMatcher<HttpRequestMatcher> {
private HttpRequestMatcher(RequestMethod requestMethod, String uri) {
private HttpRequestMatcher(RequestMethod requestMethod, String uri,
CodeBlockMatcher<?> delegate, int headerOffset) {
super(delegate, headerOffset);
this.content(requestMethod.name() + " " + uri + " HTTP/1.1");
this.content("");
}
}
/**
* Base class for table matchers.
*
* @param <T> The concrete type of the matcher
*/
public static abstract class TableMatcher<T extends TableMatcher<T>> extends
AbstractSnippetContentMatcher {
protected TableMatcher(SnippetFormat snippetFormat) {
super(snippetFormat);
}
public abstract T row(String... entries);
public abstract T configuration(String configuration);
}
/**
* A {@link Matcher} for an Asciidoctor table.
*/
public static final class AsciidoctorTableMatcher extends
AbstractSnippetContentMatcher {
TableMatcher<AsciidoctorTableMatcher> {
private AsciidoctorTableMatcher(String title, String... columns) {
super(SnippetFormats.asciidoctor());
if (StringUtils.hasText(title)) {
this.addLine("." + title);
}
@@ -216,6 +316,7 @@ public final class SnippetMatchers {
this.addLine("|===");
}
@Override
public AsciidoctorTableMatcher row(String... entries) {
for (String entry : entries) {
this.addLine(-1, "|" + entry);
@@ -224,19 +325,68 @@ public final class SnippetMatchers {
return this;
}
@Override
public AsciidoctorTableMatcher configuration(String configuration) {
this.addLine(0, configuration);
return this;
}
}
/**
* A {@link Matcher} for a Markdown table.
*/
public static final class MarkdownTableMatcher extends
TableMatcher<MarkdownTableMatcher> {
private MarkdownTableMatcher(String title, String... columns) {
super(SnippetFormats.asciidoctor());
if (StringUtils.hasText(title)) {
this.addLine(title);
}
String header = StringUtils.collectionToDelimitedString(
Arrays.asList(columns), " | ");
this.addLine(header);
List<String> components = new ArrayList<>();
for (String column : columns) {
StringBuilder dashes = new StringBuilder();
for (int i = 0; i < column.length(); i++) {
dashes.append("-");
}
components.add(dashes.toString());
}
this.addLine(StringUtils.collectionToDelimitedString(components, " | "));
this.addLine("");
}
@Override
public MarkdownTableMatcher row(String... entries) {
this.addLine(-1, StringUtils.collectionToDelimitedString(
Arrays.asList(entries), " | "));
return this;
}
@Override
public MarkdownTableMatcher configuration(String configuration) {
throw new UnsupportedOperationException(
"Markdown does not support table configuration");
}
}
/**
* A {@link Matcher} for a snippet file.
*/
public static class SnippetMatcher extends BaseMatcher<File> {
public static final class SnippetMatcher extends BaseMatcher<File> {
private final SnippetFormat snippetFormat;
private Matcher<String> expectedContents;
private SnippetMatcher(SnippetFormat snippetFormat) {
this.snippetFormat = snippetFormat;
}
@Override
public boolean matches(Object item) {
if (snippetFileExists(item)) {
@@ -285,7 +435,8 @@ public final class SnippetMatchers {
this.expectedContents.describeTo(description);
}
else {
description.appendText("Asciidoctor snippet");
description
.appendText(this.snippetFormat.getFileExtension() + " snippet");
}
}
@@ -295,4 +446,5 @@ public final class SnippetMatchers {
}
}
}

View File

@@ -0,0 +1,4 @@
{{title}}
```bash
$ curl {{url}} {{options}}
```

View File

@@ -0,0 +1,8 @@
{{title}}
```http
{{method}} {{path}} HTTP/1.1
{{#headers}}
{{name}}: {{value}}
{{/headers}}
{{requestBody}}
```

View File

@@ -0,0 +1,8 @@
{{title}}
```http
HTTP/1.1 {{statusCode}} {{statusReason}}
{{#headers}}
{{name}}: {{value}}
{{/headers}}
{{responseBody}}
```

View File

@@ -0,0 +1,5 @@
Relation | Description | Foo
-------- | ----------- | ---
{{#links}}
{{rel}} | {{description}} | {{foo}}
{{/links}}

View File

@@ -0,0 +1,6 @@
{{title}}
Relation | Description
-------- | -----------
{{#links}}
{{rel}} | {{description}}
{{/links}}

View File

@@ -0,0 +1,5 @@
Parameter | Description | Foo
--------- | ----------- | ---
{{#parameters}}
{{name}} | {{description}} | {{foo}}
{{/parameters}}

View File

@@ -0,0 +1,6 @@
{{title}}
Parameter | Description
--------- | -----------
{{#parameters}}
{{name}} | {{description}}
{{/parameters}}

View File

@@ -0,0 +1,5 @@
Path | Type | Description | Foo
---- | ---- | ----------- | ---
{{#fields}}
{{path}} | {{type}} | {{description}} | {{foo}}
{{/fields}}

View File

@@ -0,0 +1,6 @@
{{title}}
Path | Type | Description
---- | ---- | -----------
{{#fields}}
{{path}} | {{type}} | {{description}}
{{/fields}}

View File

@@ -0,0 +1,5 @@
Name | Description | Foo
---- | ----------- | ---
{{#headers}}
{{name}} | {{description}} | {{foo}}
{{/headers}}

View File

@@ -0,0 +1,6 @@
{{title}}
Name | Description
---- | -----------
{{#headers}}
{{name}} | {{description}}
{{/headers}}

View File

@@ -0,0 +1,5 @@
Parameter | Description | Foo
--------- | ----------- | ---
{{#parameters}}
{{name}} | {{description}} | {{foo}}
{{/parameters}}

View File

@@ -0,0 +1,6 @@
{{title}}
Parameter | Description
--------- | -----------
{{#parameters}}
{{name}} | {{description}}
{{/parameters}}

View File

@@ -0,0 +1,5 @@
Path | Type | Description | Foo
---- | ---- | ----------- | ---
{{#fields}}
{{path}} | {{type}} | {{description}} | {{foo}}
{{/fields}}

View File

@@ -0,0 +1,6 @@
{{title}}
Path | Type | Description
---- | ---- | -----------
{{#fields}}
{{path}} | {{type}} | {{description}}
{{/fields}}

View File

@@ -0,0 +1,5 @@
Name | Description | Foo
---- | ----------- | ---
{{#headers}}
{{name}} | {{description}} | {{foo}}
{{/headers}}

View File

@@ -0,0 +1,6 @@
{{title}}
Name | Description
---- | -----------
{{#headers}}
{{name}} | {{description}}
{{/headers}}

View File

@@ -67,8 +67,8 @@ public class MockMvcRestDocumentationConfigurer
public RequestPostProcessor beforeMockMvcCreated(
ConfigurableMockMvcBuilder<?> builder, WebApplicationContext context) {
return new ConfigurerApplyingRequestPostProcessor(this.restDocumentation,
Arrays.asList(getTemplateEngineConfigurer(),
getWriterResolverConfigurer(), snippets(), this.uriConfigurer));
Arrays.asList(snippets(), getTemplateEngineConfigurer(),
getWriterResolverConfigurer(), this.uriConfigurer));
}
@Override

View File

@@ -75,6 +75,8 @@ import static org.springframework.restdocs.request.RequestDocumentation.pathPara
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.snippet.SnippetFormats.asciidoctor;
import static org.springframework.restdocs.snippet.SnippetFormats.markdown;
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
@@ -123,6 +125,20 @@ public class MockMvcRestDocumentationIntegrationTests {
"http-request.adoc", "http-response.adoc", "curl-request.adoc");
}
@Test
public void markdownSnippetGeneration() throws Exception {
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup(this.context)
.apply(new MockMvcRestDocumentationConfigurer(this.restDocumentation)
.snippets().withEncoding("UTF-8").withFormat(markdown())).build();
mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()).andDo(document("basic-markdown"));
assertExpectedSnippetFilesExist(new File(
"build/generated-snippets/basic-markdown"), "http-request.md",
"http-response.md", "curl-request.md");
}
@Test
public void curlSnippetWithContent() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
@@ -130,10 +146,11 @@ public class MockMvcRestDocumentationIntegrationTests {
mockMvc.perform(post("/").accept(MediaType.APPLICATION_JSON).content("content"))
.andExpect(status().isOk()).andDo(document("curl-snippet-with-content"));
assertThat(new File(
"build/generated-snippets/curl-snippet-with-content/curl-request.adoc"),
is(snippet().withContents(
codeBlock("bash").content(
assertThat(
new File(
"build/generated-snippets/curl-snippet-with-content/curl-request.adoc"),
is(snippet(asciidoctor()).withContents(
codeBlock(asciidoctor(), "bash").content(
"$ curl " + "'http://localhost:8080/' -i -X POST "
+ "-H 'Accept: application/json' -d 'content'"))));
}
@@ -150,8 +167,8 @@ public class MockMvcRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc"),
is(snippet().withContents(
codeBlock("bash").content(
is(snippet(asciidoctor()).withContents(
codeBlock(asciidoctor(), "bash").content(
"$ curl "
+ "'http://localhost:8080/?foo=bar' -i -X POST "
+ "-H 'Accept: application/json' -d 'a=alpha'"))));
@@ -298,9 +315,9 @@ public class MockMvcRestDocumentationIntegrationTests {
assertThat(
new File("build/generated-snippets/original-request/http-request.adoc"),
is(snippet().withContents(
httpRequest(RequestMethod.GET, "/").header("a", "alpha")
.header("b", "bravo")
is(snippet(asciidoctor()).withContents(
httpRequest(asciidoctor(), RequestMethod.GET, "/")
.header("a", "alpha").header("b", "bravo")
.header("Content-Type", "application/json")
.header("Accept", MediaType.APPLICATION_JSON_VALUE)
.header("Host", "localhost")
@@ -310,8 +327,9 @@ public class MockMvcRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/preprocessed-request/http-request.adoc"),
is(snippet().withContents(
httpRequest(RequestMethod.GET, "/").header("b", "bravo")
is(snippet(asciidoctor()).withContents(
httpRequest(asciidoctor(), RequestMethod.GET, "/")
.header("b", "bravo")
.header("Content-Type", "application/json")
.header("Accept", MediaType.APPLICATION_JSON_VALUE)
.content(prettyPrinted))));
@@ -337,8 +355,8 @@ public class MockMvcRestDocumentationIntegrationTests {
+ "\"href\":\"href\"}]}";
assertThat(
new File("build/generated-snippets/original-response/http-response.adoc"),
is(snippet().withContents(
httpResponse(HttpStatus.OK)
is(snippet(asciidoctor()).withContents(
httpResponse(asciidoctor(), HttpStatus.OK)
.header("a", "alpha")
.header("Content-Type", "application/json")
.header(HttpHeaders.CONTENT_LENGTH,
@@ -348,8 +366,8 @@ public class MockMvcRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/preprocessed-response/http-response.adoc"),
is(snippet().withContents(
httpResponse(HttpStatus.OK)
is(snippet(asciidoctor()).withContents(
httpResponse(asciidoctor(), HttpStatus.OK)
.header("Content-Type", "application/json")
.header(HttpHeaders.CONTENT_LENGTH,
prettyPrinted.getBytes().length)
@@ -376,7 +394,7 @@ public class MockMvcRestDocumentationIntegrationTests {
}
assertThat(new File(
"build/generated-snippets/custom-snippet-template/curl-request.adoc"),
is(snippet().withContents(equalTo("Custom curl request"))));
is(snippet(asciidoctor()).withContents(equalTo("Custom curl request"))));
mockMvc.perform(get("/")).andDo(
document(
@@ -395,9 +413,9 @@ public class MockMvcRestDocumentationIntegrationTests {
.andExpect(status().isOk()).andDo(document("custom-context-path"));
assertThat(
new File("build/generated-snippets/custom-context-path/curl-request.adoc"),
is(snippet()
is(snippet(asciidoctor())
.withContents(
codeBlock("bash")
codeBlock(asciidoctor(), "bash")
.content(
"$ curl 'http://localhost:8080/custom/' -i -H 'Accept: application/json'"))));
}

View File

@@ -51,8 +51,8 @@ public final class RestAssuredRestDocumentationConfigurer
RestAssuredRestDocumentationConfigurer(RestDocumentation restDocumentation) {
this.restDocumentation = restDocumentation;
this.configurers = Arrays.asList(getTemplateEngineConfigurer(),
getWriterResolverConfigurer(), snippets());
this.configurers = Arrays.asList(snippets(), getTemplateEngineConfigurer(),
getWriterResolverConfigurer());
}
@Override

View File

@@ -70,6 +70,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.requestP
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.modifyUris;
import static org.springframework.restdocs.snippet.SnippetFormats.asciidoctor;
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
@@ -114,8 +115,8 @@ public class RestAssuredRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/curl-snippet-with-content/curl-request.adoc"),
is(snippet().withContents(
codeBlock("bash").content(
is(snippet(asciidoctor()).withContents(
codeBlock(asciidoctor(), "bash").content(
"$ curl 'http://localhost:" + this.port + "/' -i "
+ "-X POST -H 'Accept: application/json' "
+ "-H 'Content-Type: " + contentType + "' "
@@ -133,8 +134,8 @@ public class RestAssuredRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/curl-snippet-with-query-string/curl-request.adoc"),
is(snippet().withContents(
codeBlock("bash").content(
is(snippet(asciidoctor()).withContents(
codeBlock(asciidoctor(), "bash").content(
"$ curl " + "'http://localhost:" + this.port
+ "/?foo=bar' -i -X POST "
+ "-H 'Accept: application/json' "
@@ -261,9 +262,9 @@ public class RestAssuredRestDocumentationIntegrationTests {
.then().statusCode(200);
assertThat(
new File("build/generated-snippets/original-request/http-request.adoc"),
is(snippet()
is(snippet(asciidoctor())
.withContents(
httpRequest(RequestMethod.GET, "/")
httpRequest(asciidoctor(), RequestMethod.GET, "/")
.header("a", "alpha")
.header("b", "bravo")
.header("Accept",
@@ -277,9 +278,9 @@ public class RestAssuredRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/preprocessed-request/http-request.adoc"),
is(snippet()
is(snippet(asciidoctor())
.withContents(
httpRequest(RequestMethod.GET, "/")
httpRequest(asciidoctor(), RequestMethod.GET, "/")
.header("b", "bravo")
.header("Accept",
MediaType.APPLICATION_JSON_VALUE)
@@ -308,8 +309,8 @@ public class RestAssuredRestDocumentationIntegrationTests {
assertThat(
new File(
"build/generated-snippets/preprocessed-response/http-response.adoc"),
is(snippet().withContents(
httpResponse(HttpStatus.OK)
is(snippet(asciidoctor()).withContents(
httpResponse(asciidoctor(), HttpStatus.OK)
.header("Foo", "https://api.example.com/foo/bar")
.header("Content-Type", "application/json;charset=UTF-8")
.header(HttpHeaders.CONTENT_LENGTH,
@@ -335,7 +336,7 @@ public class RestAssuredRestDocumentationIntegrationTests {
}
assertThat(new File(
"build/generated-snippets/custom-snippet-template/curl-request.adoc"),
is(snippet().withContents(equalTo("Custom curl request"))));
is(snippet(asciidoctor()).withContents(equalTo("Custom curl request"))));
}
private void assertExpectedSnippetFilesExist(File directory, String... snippets) {