From 5d092608705fb7d4396e78530b51296d0c97a989 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 25 Mar 2022 10:12:42 +0000 Subject: [PATCH] Reinstate support for REST Assured Closes gh-29543 Closes gh-30426 --- .../spring-boot-dependencies/build.gradle | 13 +++ .../spring-boot-docs/build.gradle | 1 + .../docs/asciidoc/anchor-rewrite.properties | 1 + .../src/docs/asciidoc/features/testing.adoc | 15 ++- .../MyRestDocsConfiguration.java | 32 ++++++ .../MyUserDocumentationTests.java | 49 ++++++++ .../MyRestDocsConfiguration.kt | 31 +++++ .../MyUserDocumentationTests.kt | 44 ++++++++ .../build.gradle | 1 + .../restdocs/AutoConfigureRestDocs.java | 13 ++- .../restdocs/RestDocsAutoConfiguration.java | 30 +++++ .../RestDocsRestAssuredBuilderCustomizer.java | 51 +++++++++ ...ocsRestAssuredConfigurationCustomizer.java | 41 +++++++ ...AdvancedConfigurationIntegrationTests.java | 106 ++++++++++++++++++ ...DocsAutoConfigurationIntegrationTests.java | 76 +++++++++++++ 15 files changed, 499 insertions(+), 5 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt create mode 100644 spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java create mode 100644 spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationIntegrationTests.java diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index 981254b821..102cf07957 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -1141,6 +1141,19 @@ bom { ] } } + library("REST Assured", "5.0.0") { + group("io.rest-assured") { + modules = [ + "json-path", + "json-schema-validator", + "rest-assured", + "scala-support", + "spring-mock-mvc", + "spring-web-test-client", + "xml-path" + ] + } + } library("RSocket", "1.1.1") { group("io.rsocket") { imports = [ diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index dceae8764e..ba758eb2d8 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -152,6 +152,7 @@ dependencies { implementation("org.springframework.restdocs:spring-restdocs-mockmvc") { exclude group: "javax.servlet", module: "javax.servlet-api" } + implementation("org.springframework.restdocs:spring-restdocs-restassured") implementation("org.springframework.restdocs:spring-restdocs-webtestclient") implementation("org.springframework.security:spring-security-config") implementation("org.springframework.security:spring-security-oauth2-client") diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties index eb58e9ea4f..731838b9ce 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/anchor-rewrite.properties @@ -349,6 +349,7 @@ boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-clien boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-docs=features.testing.spring-boot-applications.autoconfigured-spring-restdocs boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-docs-mock-mvc=features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-mock-mvc boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-docs-web-test-client=features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-web-test-client +boot-features-testing-spring-boot-applications-testing-autoconfigured-rest-docs-rest-assured=features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-rest-assured boot-features-testing-spring-boot-applications-testing-autoconfigured-webservices=features.testing.spring-boot-applications.autoconfigured-webservices boot-features-testing-spring-boot-applications-testing-auto-configured-additional-auto-config=features.testing.spring-boot-applications.additional-autoconfiguration-and-slicing boot-features-testing-spring-boot-applications-testing-user-configuration=features.testing.spring-boot-applications.user-configuration-and-slicing diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc index ab283845c6..02e5af2034 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc @@ -646,7 +646,7 @@ include::code:MyRestClientTests[] [[features.testing.spring-boot-applications.autoconfigured-spring-restdocs]] ==== Auto-configured Spring REST Docs Tests -You can use the `@AutoConfigureRestDocs` annotation to use {spring-restdocs}[Spring REST Docs] in your tests with Mock MVC or WebTestClient. +You can use the `@AutoConfigureRestDocs` annotation to use {spring-restdocs}[Spring REST Docs] in your tests with Mock MVC, REST Assured, or WebTestClient. It removes the need for the JUnit extension in Spring REST Docs. `@AutoConfigureRestDocs` can be used to override the default output directory (`target/generated-snippets` if you are using Maven or `build/generated-snippets` if you are using Gradle). @@ -691,6 +691,19 @@ include::code:MyWebTestClientBuilderCustomizerConfiguration[] +[[features.testing.spring-boot-applications.autoconfigured-spring-restdocs.with-rest-assured]] +===== Auto-configured Spring REST Docs Tests with REST Assured +`@AutoConfigureRestDocs` makes a `RequestSpecification` bean, preconfigured to use Spring REST Docs, available to your tests. +You can inject it by using `@Autowired` and use it in your tests as you normally would when using REST Assured and Spring REST Docs, as shown in the following example: + +include::code:MyUserDocumentationTests[] + +If you require more control over Spring REST Docs configuration than offered by the attributes of `@AutoConfigureRestDocs`, a `RestDocsRestAssuredConfigurationCustomizer` bean can be used, as shown in the following example: + +include::code:MyRestDocsConfiguration[] + + + [[features.testing.spring-boot-applications.autoconfigured-webservices]] ==== Auto-configured Spring Web Services Tests diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.java new file mode 100644 index 0000000000..699b5e3747 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured; + +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer; +import org.springframework.restdocs.templates.TemplateFormats; + +@TestConfiguration(proxyBeanMethods = false) +public class MyRestDocsConfiguration implements RestDocsRestAssuredConfigurationCustomizer { + + @Override + public void customize(RestAssuredRestDocumentationConfigurer configurer) { + configurer.snippets().withTemplateFormat(TemplateFormats.markdown()); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.java new file mode 100644 index 0000000000..9799fbfa4b --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured; + +import io.restassured.specification.RequestSpecification; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.web.server.LocalServerPort; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureRestDocs +class MyUserDocumentationTests { + + @Test + void listUsers(@Autowired RequestSpecification documentationSpec, @LocalServerPort int port) { + // @formatter:off + given(documentationSpec) + .filter(document("list-users")) + .when() + .port(port) + .get("/") + .then().assertThat() + .statusCode(is(200)); + // @formatter:on + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt new file mode 100644 index 0000000000..7f332a838b --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyRestDocsConfiguration.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured + +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsRestAssuredConfigurationCustomizer +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer +import org.springframework.restdocs.templates.TemplateFormats + +@TestConfiguration(proxyBeanMethods = false) +class MyRestDocsConfiguration : RestDocsRestAssuredConfigurationCustomizer { + + override fun customize(configurer: RestAssuredRestDocumentationConfigurer) { + configurer.snippets().withTemplateFormat(TemplateFormats.markdown()) + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt new file mode 100644 index 0000000000..1a8be789a4 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/kotlin/org/springframework/boot/docs/features/testing/springbootapplications/autoconfiguredspringrestdocs/withrestassured/MyUserDocumentationTests.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.docs.features.testing.springbootapplications.autoconfiguredspringrestdocs.withrestassured + +import io.restassured.RestAssured +import io.restassured.specification.RequestSpecification +import org.hamcrest.Matchers +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs +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.restdocs.restassured.RestAssuredRestDocumentation + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureRestDocs +class MyUserDocumentationTests { + + @Test + fun listUsers(@Autowired documentationSpec: RequestSpecification?, @LocalServerPort port: Int) { + RestAssured.given(documentationSpec) + .filter(RestAssuredRestDocumentation.document("list-users")) + .`when`() + .port(port)["/"] + .then().assertThat() + .statusCode(Matchers.`is`(200)) + } + +} \ No newline at end of file diff --git a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle index bf02c5e8b0..615c1c88c4 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/build.gradle +++ b/spring-boot-project/spring-boot-test-autoconfigure/build.gradle @@ -49,6 +49,7 @@ dependencies { optional("org.springframework.restdocs:spring-restdocs-mockmvc") { exclude group: "javax.servlet", module: "javax.servlet-api" } + optional("org.springframework.restdocs:spring-restdocs-restassured") optional("org.springframework.restdocs:spring-restdocs-webtestclient") optional("org.springframework.security:spring-security-config") optional("org.springframework.security:spring-security-test") diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/AutoConfigureRestDocs.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/AutoConfigureRestDocs.java index 786310903f..a4c11737ef 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/AutoConfigureRestDocs.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/AutoConfigureRestDocs.java @@ -23,6 +23,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import io.restassured.RestAssured; + import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.autoconfigure.properties.PropertyMapping; import org.springframework.context.annotation.Import; @@ -33,19 +35,22 @@ import org.springframework.test.web.servlet.MockMvc; /** * Annotation that can be applied to a test class to enable and configure * auto-configuration of Spring REST Docs. The auto-configuration sets up - * {@link MockMvc}-based testing of a servlet web application or - * {@link WebTestClient}-based testing of a reactive web application. + * {@link MockMvc}-based testing of a servlet web application, {@link WebTestClient}-based + * testing of a reactive web application, or {@link RestAssured}-based testing of any web + * application over HTTP. *

* Allows configuration of the output directory and the host, scheme, and port of * generated URIs. When further configuration is required a - * {@link RestDocsMockMvcConfigurationCustomizer} or - * {@link RestDocsWebTestClientConfigurationCustomizer} bean can be used. + * {@link RestDocsMockMvcConfigurationCustomizer}, + * {@link RestDocsWebTestClientConfigurationCustomizer}, or + * {@link RestDocsRestAssuredConfigurationCustomizer} bean can be used. * * @author Andy Wilkinson * @since 1.4.0 * @see RestDocsAutoConfiguration * @see RestDocsMockMvcConfigurationCustomizer * @see RestDocsWebTestClientConfigurationCustomizer + * @see RestDocsRestAssuredConfigurationCustomizer */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java index eabc7930c7..31f42ff2ca 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java @@ -16,6 +16,9 @@ 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.AutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -30,6 +33,8 @@ 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.restassured.RestAssuredRestDocumentation; +import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer; import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentation; import org.springframework.restdocs.webtestclient.WebTestClientRestDocumentationConfigurer; @@ -72,6 +77,31 @@ public class RestDocsAutoConfiguration { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass({ RequestSpecification.class, RestAssuredRestDocumentation.class }) + @EnableConfigurationProperties(RestDocsProperties.class) + static class RestDocsRestAssuredConfiguration { + + @Bean + @ConditionalOnMissingBean + RequestSpecification restDocsRestAssuredConfigurer( + ObjectProvider configurationCustomizers, + RestDocumentationContextProvider contextProvider) { + RestAssuredRestDocumentationConfigurer configurer = RestAssuredRestDocumentation + .documentationConfiguration(contextProvider); + configurationCustomizers.orderedStream() + .forEach((configurationCustomizer) -> configurationCustomizer.customize(configurer)); + return new RequestSpecBuilder().addFilter(configurer).build(); + } + + @Bean + RestDocsRestAssuredBuilderCustomizer restAssuredBuilderCustomizer(RestDocsProperties properties, + RequestSpecification configurer) { + return new RestDocsRestAssuredBuilderCustomizer(properties, configurer); + } + + } + @Configuration(proxyBeanMethods = false) @ConditionalOnClass(WebTestClientRestDocumentation.class) @ConditionalOnWebApplication(type = Type.REACTIVE) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java new file mode 100644 index 0000000000..493e897c62 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredBuilderCustomizer.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.boot.context.properties.PropertyMapper; +import org.springframework.util.StringUtils; + +/** + * A customizer that configures Spring REST Docs with REST Assured. + * + * @author Eddú Meléndez + */ +class RestDocsRestAssuredBuilderCustomizer implements InitializingBean { + + private final RestDocsProperties properties; + + private final RequestSpecification delegate; + + RestDocsRestAssuredBuilderCustomizer(RestDocsProperties properties, RequestSpecification delegate) { + this.properties = properties; + this.delegate = delegate; + } + + @Override + public void afterPropertiesSet() throws Exception { + PropertyMapper map = PropertyMapper.get(); + String host = this.properties.getUriHost(); + map.from(this.properties::getUriScheme) + .when((scheme) -> StringUtils.hasText(scheme) && StringUtils.hasText(host)) + .to((scheme) -> this.delegate.baseUri(scheme + "://" + host)); + map.from(this.properties::getUriPort).whenNonNull().to(this.delegate::port); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java new file mode 100644 index 0000000000..c15496100e --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsRestAssuredConfigurationCustomizer.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.restassured.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 @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-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java new file mode 100644 index 0000000000..c869a94724 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -0,0 +1,106 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.testsupport.BuildOutput; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.annotation.Bean; +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; +import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; +import org.springframework.restdocs.templates.TemplateFormats; +import org.springframework.util.FileSystemUtils; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; + +/** + * Integration tests for advanced configuration of + * {@link AutoConfigureRestDocs @AutoConfigureRestDocs} with REST Assured. + * + * @author Eddú Meléndez + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureRestDocs +class RestAssuredRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests { + + @LocalServerPort + private int port; + + @Autowired + private RequestSpecification documentationSpec; + + private File generatedSnippets; + + @BeforeEach + void deleteSnippets() { + this.generatedSnippets = new File(new BuildOutput(getClass()).getRootLocation(), "generated-snippets"); + FileSystemUtils.deleteRecursively(this.generatedSnippets); + } + + @Test + void snippetGeneration() { + 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(this.generatedSnippets, "default-snippets"); + assertThat(defaultSnippetsDir).exists(); + assertThat(contentOf(new File(defaultSnippetsDir, "curl-request.md"))).contains("'https://api.example.com/'"); + assertThat(contentOf(new File(defaultSnippetsDir, "http-request.md"))).contains("api.example.com"); + assertThat(new File(defaultSnippetsDir, "http-response.md")).isFile(); + assertThat(new File(defaultSnippetsDir, "response-fields.md")).isFile(); + } + + @TestConfiguration(proxyBeanMethods = false) + static class CustomizationConfiguration { + + @Bean + RestDocumentationResultHandler restDocumentation() { + return MockMvcRestDocumentation.document("{method-name}"); + } + + @Bean + RestDocsRestAssuredConfigurationCustomizer templateFormatCustomizer() { + return (configurer) -> configurer.snippets().withTemplateFormat(TemplateFormats.markdown()); + } + + @Bean + RestDocsRestAssuredConfigurationCustomizer defaultSnippetsCustomizer() { + return (configurer) -> configurer.snippets() + .withAdditionalDefaults(responseFields(fieldWithPath("_links.self").description("Main URL"))); + } + + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationIntegrationTests.java new file mode 100644 index 0000000000..092daf284e --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/RestAssuredRestDocsAutoConfigurationIntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012-2022 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 + * + * https://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.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +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.testsupport.BuildOutput; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.util.FileSystemUtils; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.contentOf; +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.modifyUris; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document; + +/** + * Integration tests for {@link RestDocsAutoConfiguration} with REST Assured. + * + * @author Eddú Meléndez + */ +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureRestDocs +class RestAssuredRestDocsAutoConfigurationIntegrationTests { + + @LocalServerPort + private int port; + + @Autowired + private RequestSpecification documentationSpec; + + private File generatedSnippets; + + @BeforeEach + void deleteSnippets() { + this.generatedSnippets = new File(new BuildOutput(getClass()).getRootLocation(), "generated-snippets"); + FileSystemUtils.deleteRecursively(this.generatedSnippets); + } + + @Test + void defaultSnippetsAreWritten() { + 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(this.generatedSnippets, "default-snippets"); + assertThat(defaultSnippetsDir).exists(); + assertThat(contentOf(new File(defaultSnippetsDir, "curl-request.adoc"))).contains("'https://api.example.com/'"); + assertThat(contentOf(new File(defaultSnippetsDir, "http-request.adoc"))).contains("api.example.com"); + assertThat(new File(defaultSnippetsDir, "http-response.adoc")).isFile(); + } + +}