Commit 98b2267a authored by Eddú Meléndez's avatar Eddú Meléndez Committed by Andy Wilkinson

Add auto-configuration for REST Docs with REST Assured

See gh-9643
parent a1e26eae
......@@ -150,6 +150,7 @@
<quartz.version>2.3.0</quartz.version>
<querydsl.version>4.1.4</querydsl.version>
<reactor-bom.version>Bismuth-RC1</reactor-bom.version>
<rest-assured.version>3.0.2</rest-assured.version>
<reactive-streams.version>1.0.1</reactive-streams.version>
<rxjava.version>1.3.2</rxjava.version>
<rxjava-adapter.version>1.2.1</rxjava-adapter.version>
......@@ -906,6 +907,11 @@
<artifactId>rxjava</artifactId>
<version>${rxjava2.version}</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
</dependency>
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
......
......@@ -70,7 +70,7 @@
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck" />
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStaticImportCheck">
<property name="excludes"
value="org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.ArgumentMatchers.*, org.mockito.Matchers.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.ExpectedCount.*, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*" />
value="io.restassured.RestAssured.*, org.assertj.core.api.Assertions.*, org.junit.Assert.*, org.junit.Assume.*, org.junit.internal.matchers.ThrowableMessageMatcher.*, org.hamcrest.CoreMatchers.*, org.hamcrest.Matchers.*, org.springframework.boot.configurationprocessor.ConfigurationMetadataMatchers.*, org.springframework.boot.configurationprocessor.TestCompiler.*, org.springframework.boot.test.autoconfigure.AutoConfigurationImportedCondition.*, org.mockito.Mockito.*, org.mockito.BDDMockito.*, org.mockito.ArgumentMatchers.*, org.mockito.Matchers.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.*, org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.*, org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*, org.springframework.test.web.servlet.result.MockMvcResultMatchers.*, org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*, org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*, org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo, org.springframework.test.web.client.ExpectedCount.*, org.springframework.test.web.client.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*" />
</module>
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck" >
<property name="illegalPkgs" value="com.google.common"/>
......
......@@ -59,6 +59,11 @@
<artifactId>json-path</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
......@@ -146,6 +151,11 @@
<artifactId>spring-restdocs-mockmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-restassured</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
......@@ -198,6 +208,11 @@
<artifactId>commons-pool2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
......
......@@ -16,8 +16,12 @@
package org.springframework.boot.test.autoconfigure.restdocs;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.specification.RequestSpecification;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
......@@ -29,18 +33,25 @@ import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentation;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring REST Docs.
*
* @author Andy Wilkinson
* @author Eddú Meléndez
* @since 1.4.0
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties
public class RestDocsAutoConfiguration {
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(MockMvcRestDocumentation.class)
static class RestDocsMockMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class)
public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer(
......@@ -65,4 +76,34 @@ public class RestDocsAutoConfiguration {
resultHandler.getIfAvailable());
}
}
@Configuration
@ConditionalOnClass({ RequestSpecification.class, RestAssuredRestDocumentation.class })
static class RestDocsRestAssuredAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RequestSpecification.class)
public RequestSpecification restDocsRestAssuredConfigurer(
ObjectProvider<RestDocsRestAssuredConfigurationCustomizer> configurationCustomizerProvider,
RestDocumentationContextProvider contextProvider) {
RestAssuredRestDocumentationConfigurer configurer = RestAssuredRestDocumentation
.documentationConfiguration(contextProvider);
RestDocsRestAssuredConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider.getIfAvailable();
if (configurationCustomizer != null) {
configurationCustomizer.customize(configurer);
}
return new RequestSpecBuilder().addFilter(configurer).build();
}
@Bean
@ConfigurationProperties(prefix = "spring.test.restdocs")
public RestDocsRestAssuredBuilderCustomizer restAssuredBuilderCustomizer(
RequestSpecification configurer) {
return new RestDocsRestAssuredBuilderCustomizer(configurer);
}
}
}
/*
* Copyright 2012-2017 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.boot.test.autoconfigure.restdocs;
import io.restassured.specification.RequestSpecification;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;
/**
* A customizer that configures Spring REST Docs with RestAssured.
*
* @author Eddú Meléndez
*/
class RestDocsRestAssuredBuilderCustomizer implements InitializingBean {
private final RequestSpecification delegate;
private String uriScheme;
private String uriHost;
private Integer uriPort;
RestDocsRestAssuredBuilderCustomizer(RequestSpecification delegate) {
this.delegate = delegate;
}
public String getUriScheme() {
return this.uriScheme;
}
public void setUriScheme(String uriScheme) {
this.uriScheme = uriScheme;
}
public String getUriHost() {
return this.uriHost;
}
public void setUriHost(String uriHost) {
this.uriHost = uriHost;
}
public Integer getUriPort() {
return this.uriPort;
}
public void setUriPort(Integer uriPort) {
this.uriPort = uriPort;
}
@Override
public void afterPropertiesSet() throws Exception {
if (StringUtils.hasText(this.uriScheme) && StringUtils.hasText(this.uriHost)) {
this.delegate.baseUri(this.uriScheme + "://" + this.uriHost);
}
if (this.uriPort != null) {
this.delegate.port(this.uriPort);
}
}
}
/*
* Copyright 2012-2017 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.boot.test.autoconfigure.restdocs;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
/**
* A customizer for {@link RestAssuredRestDocumentationConfigurer}. If a
* {@code RestDocsRestAssuredConfigurationCustomizer} bean is found in the application
* context it will be {@link #customize called} to customize the
* {@code RestAssuredRestDocumentationConfigurer} before it is applied. Intended for use
* only when the attributes on {@link AutoConfigureRestDocs} do not provide sufficient
* customization.
*
* @author Eddú Meléndez
* @since 2.0.0
*/
@FunctionalInterface
public interface RestDocsRestAssuredConfigurationCustomizer {
/**
* Customize the given {@code configurer}.
* @param configurer the configurer
*/
void customize(RestAssuredRestDocumentationConfigurer configurer);
}
/*
* Copyright 2012-2017 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.boot.test.autoconfigure.restdocs;
import java.io.File;
import io.restassured.specification.RequestSpecification;
import org.assertj.core.api.Condition;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
import org.springframework.restdocs.templates.TemplateFormats;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.FileSystemUtils;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.modifyUris;
/**
* Tests for {@link AutoConfigureRestDocs}.
*
* @author Eddú Meléndez
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
public class RestAssuredAutoConfigurationAdvancedConfigurationIntegrationTests {
@LocalServerPort
private int port;
@Before
public void deleteSnippets() {
FileSystemUtils.deleteRecursively(new File("target/generated-snippets"));
}
@Autowired
private RequestSpecification documentationSpec;
@Test
public void snippetGeneration() throws Exception {
given(this.documentationSpec)
.filter(document("default-snippets", preprocessRequest(modifyUris()
.scheme("https").host("api.example.com").removePort()))).when()
.port(this.port).get("/").then().assertThat().statusCode(is(200));
File defaultSnippetsDir = new File("target/generated-snippets/default-snippets");
assertThat(defaultSnippetsDir).exists();
assertThat(new File(defaultSnippetsDir, "curl-request.md")).has(
contentContaining("'https://api.example.com/'"));
assertThat(new File(defaultSnippetsDir, "http-request.md")).has(
contentContaining("api.example.com"));
assertThat(new File(defaultSnippetsDir, "http-response.md")).isFile();
}
private Condition<File> contentContaining(String toContain) {
return new ContentContainingCondition(toContain);
}
@TestConfiguration
public static class CustomizationConfiguration implements
RestDocsRestAssuredConfigurationCustomizer {
@Override
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
}
}
}
/*
* Copyright 2012-2017 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.boot.test.autoconfigure.restdocs;
import java.io.File;
import io.restassured.specification.RequestSpecification;
import org.assertj.core.api.Condition;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.FileSystemUtils;
import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
import static org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.modifyUris;
/**
* Tests for {@link RestDocsAutoConfiguration}
*
* @author Eddú Meléndez
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureRestDocs
public class RestAssuredAutoConfigurationIntegrationTests {
@LocalServerPort
private int port;
@Before
public void deleteSnippets() {
FileSystemUtils.deleteRecursively(new File("target/generated-snippets"));
}
@Autowired
private RequestSpecification documentationSpec;
@Test
public void defaultSnippetsAreWritten() throws Exception {
given(this.documentationSpec)
.filter(document("default-snippets", preprocessRequest(modifyUris()
.scheme("https").host("api.example.com").removePort()))).when()
.port(this.port).get("/").then().assertThat().statusCode(is(200));
File defaultSnippetsDir = new File("target/generated-snippets/default-snippets");
assertThat(defaultSnippetsDir).exists();
assertThat(new File(defaultSnippetsDir, "curl-request.adoc")).has(
contentContaining("'https://api.example.com/'"));
assertThat(new File(defaultSnippetsDir, "http-request.adoc")).has(
contentContaining("api.example.com"));
assertThat(new File(defaultSnippetsDir, "http-response.adoc")).isFile();
}
private Condition<File> contentContaining(String toContain) {
return new ContentContainingCondition(toContain);
}
}
......@@ -17,13 +17,14 @@
package org.springframework.boot.test.autoconfigure.restdocs;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
/**
* Test application used with {@link AutoConfigureRestDocs} tests.
*
* @author Andy Wilkinson
*/
@SpringBootApplication
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class RestDocsTestApplication {
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment