diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 3d506a0a4900..f2a820ec9221 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -142,6 +142,7 @@ 2.3.0 4.1.4 Bismuth-BUILD-SNAPSHOT + 3.0.2 1.3.0 1.2.1 2.1.1 @@ -868,6 +869,11 @@ rxjava ${rxjava2.version} + + io.rest-assured + rest-assured + ${restassured.version} + io.searchbox jest diff --git a/spring-boot-parent/src/checkstyle/checkstyle.xml b/spring-boot-parent/src/checkstyle/checkstyle.xml index 5c1d21d34646..20f0028d378d 100644 --- a/spring-boot-parent/src/checkstyle/checkstyle.xml +++ b/spring-boot-parent/src/checkstyle/checkstyle.xml @@ -70,7 +70,7 @@ + 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.mockmvc.MockMvcRestDocumentation.*, org.springframework.restdocs.hypermedia.HypermediaDocumentation.*, org.springframework.restdocs.operation.preprocess.Preprocessors.*, org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.*, org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.*, 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.match.MockRestRequestMatchers.*, org.springframework.test.web.client.response.MockRestResponseCreators.*" /> diff --git a/spring-boot-test-autoconfigure/pom.xml b/spring-boot-test-autoconfigure/pom.xml index 51d395e1e6ce..e36ee7c5ed5b 100644 --- a/spring-boot-test-autoconfigure/pom.xml +++ b/spring-boot-test-autoconfigure/pom.xml @@ -54,6 +54,11 @@ json-path true + + io.rest-assured + rest-assured + true + net.sourceforge.htmlunit htmlunit @@ -141,6 +146,11 @@ spring-restdocs-mockmvc true + + org.springframework.restdocs + spring-restdocs-restassured + true + org.springframework.security spring-security-config @@ -188,6 +198,11 @@ commons-pool2 test + + org.apache.tomcat.embed + tomcat-embed-core + true + org.aspectj aspectjrt diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java index e31c4237512c..c1136f96dddd 100644 --- a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java @@ -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,40 +33,77 @@ 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 { - @Bean - @ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class) - public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer( - ObjectProvider configurationCustomizerProvider, - RestDocumentationContextProvider contextProvider) { - MockMvcRestDocumentationConfigurer configurer = MockMvcRestDocumentation - .documentationConfiguration(contextProvider); - RestDocsMockMvcConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider - .getIfAvailable(); - if (configurationCustomizer != null) { - configurationCustomizer.customize(configurer); + @Configuration + @ConditionalOnWebApplication(type = Type.SERVLET) + @ConditionalOnClass(MockMvcRestDocumentation.class) + static class RestDocsMockMvcAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class) + public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer( + ObjectProvider configurationCustomizerProvider, + RestDocumentationContextProvider contextProvider) { + MockMvcRestDocumentationConfigurer configurer = MockMvcRestDocumentation + .documentationConfiguration(contextProvider); + RestDocsMockMvcConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider + .getIfAvailable(); + if (configurationCustomizer != null) { + configurationCustomizer.customize(configurer); + } + return configurer; + } + + @Bean + @ConfigurationProperties(prefix = "spring.test.restdocs") + public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer( + MockMvcRestDocumentationConfigurer configurer, + ObjectProvider resultHandler) { + return new RestDocsMockMvcBuilderCustomizer(configurer, + resultHandler.getIfAvailable()); } - return configurer; + } - @Bean - @ConfigurationProperties(prefix = "spring.test.restdocs") - public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer( - MockMvcRestDocumentationConfigurer configurer, - ObjectProvider resultHandler) { - return new RestDocsMockMvcBuilderCustomizer(configurer, - resultHandler.getIfAvailable()); + @Configuration + @ConditionalOnClass({ RequestSpecification.class, RestAssuredRestDocumentation.class }) + static class RestDocsRestAssuredAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(RequestSpecification.class) + public RequestSpecification restDocsRestAssuredConfigurer( + ObjectProvider 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); + } + } + } diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java new file mode 100644 index 000000000000..f5b5335a3312 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java @@ -0,0 +1,77 @@ +/* + * 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); + } + } + +} diff --git a/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java new file mode 100644 index 000000000000..01aaba49df5d --- /dev/null +++ b/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java @@ -0,0 +1,41 @@ +/* + * 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); + +} diff --git a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index 0d335481a554..ce87c81d4e0c 100644 --- a/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -145,4 +145,4 @@ org.springframework.test.context.TestExecutionListener=\ org.springframework.boot.test.autoconfigure.restdocs.RestDocsTestExecutionListener,\ org.springframework.boot.test.autoconfigure.web.client.MockRestServiceServerResetTestExecutionListener,\ org.springframework.boot.test.autoconfigure.web.servlet.MockMvcPrintOnlyOnFailureTestExecutionListener,\ -org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener \ No newline at end of file +org.springframework.boot.test.autoconfigure.web.servlet.WebDriverTestExecutionListener diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationAdvancedConfigurationIntegrationTests.java new file mode 100644 index 000000000000..86e8db83889b --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -0,0 +1,95 @@ +/* + * 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 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()); + } + + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationIntegrationTests.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationIntegrationTests.java new file mode 100644 index 000000000000..adc73336f442 --- /dev/null +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredAutoConfigurationIntegrationTests.java @@ -0,0 +1,81 @@ +/* + * 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 contentContaining(String toContain) { + return new ContentContainingCondition(toContain); + } + +} diff --git a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsTestApplication.java b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsTestApplication.java index eef3c6095fbd..ce7d6356162c 100644 --- a/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsTestApplication.java +++ b/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsTestApplication.java @@ -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 { }