Skip to content

Commit 98b2267

Browse files
eddumelendezwilkinsona
authored andcommitted
Add auto-configuration for REST Docs with REST Assured
See gh-9643
1 parent a1e26ea commit 98b2267

File tree

9 files changed

+379
-22
lines changed

9 files changed

+379
-22
lines changed

spring-boot-dependencies/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
<quartz.version>2.3.0</quartz.version>
151151
<querydsl.version>4.1.4</querydsl.version>
152152
<reactor-bom.version>Bismuth-RC1</reactor-bom.version>
153+
<rest-assured.version>3.0.2</rest-assured.version>
153154
<reactive-streams.version>1.0.1</reactive-streams.version>
154155
<rxjava.version>1.3.2</rxjava.version>
155156
<rxjava-adapter.version>1.2.1</rxjava-adapter.version>
@@ -906,6 +907,11 @@
906907
<artifactId>rxjava</artifactId>
907908
<version>${rxjava2.version}</version>
908909
</dependency>
910+
<dependency>
911+
<groupId>io.rest-assured</groupId>
912+
<artifactId>rest-assured</artifactId>
913+
<version>${rest-assured.version}</version>
914+
</dependency>
909915
<dependency>
910916
<groupId>io.searchbox</groupId>
911917
<artifactId>jest</artifactId>

spring-boot-parent/src/checkstyle/checkstyle.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck" />
7171
<module name="com.puppycrawl.tools.checkstyle.checks.imports.AvoidStaticImportCheck">
7272
<property name="excludes"
73-
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.*" />
73+
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.*" />
7474
</module>
7575
<module name="com.puppycrawl.tools.checkstyle.checks.imports.IllegalImportCheck" >
7676
<property name="illegalPkgs" value="com.google.common"/>

spring-boot-test-autoconfigure/pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@
5959
<artifactId>json-path</artifactId>
6060
<optional>true</optional>
6161
</dependency>
62+
<dependency>
63+
<groupId>io.rest-assured</groupId>
64+
<artifactId>rest-assured</artifactId>
65+
<optional>true</optional>
66+
</dependency>
6267
<dependency>
6368
<groupId>net.sourceforge.htmlunit</groupId>
6469
<artifactId>htmlunit</artifactId>
@@ -146,6 +151,11 @@
146151
<artifactId>spring-restdocs-mockmvc</artifactId>
147152
<optional>true</optional>
148153
</dependency>
154+
<dependency>
155+
<groupId>org.springframework.restdocs</groupId>
156+
<artifactId>spring-restdocs-restassured</artifactId>
157+
<optional>true</optional>
158+
</dependency>
149159
<dependency>
150160
<groupId>org.springframework.security</groupId>
151161
<artifactId>spring-security-config</artifactId>
@@ -198,6 +208,11 @@
198208
<artifactId>commons-pool2</artifactId>
199209
<scope>test</scope>
200210
</dependency>
211+
<dependency>
212+
<groupId>org.apache.tomcat.embed</groupId>
213+
<artifactId>tomcat-embed-core</artifactId>
214+
<optional>true</optional>
215+
</dependency>
201216
<dependency>
202217
<groupId>org.aspectj</groupId>
203218
<artifactId>aspectjrt</artifactId>

spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/restdocs/RestDocsAutoConfiguration.java

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616

1717
package org.springframework.boot.test.autoconfigure.restdocs;
1818

19+
import io.restassured.builder.RequestSpecBuilder;
20+
import io.restassured.specification.RequestSpecification;
21+
1922
import org.springframework.beans.factory.ObjectProvider;
2023
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
24+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2125
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2226
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
2327
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
@@ -29,40 +33,77 @@
2933
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
3034
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
3135
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
36+
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentation;
37+
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
3238

3339
/**
3440
* {@link EnableAutoConfiguration Auto-configuration} for Spring REST Docs.
3541
*
3642
* @author Andy Wilkinson
43+
* @author Eddú Meléndez
3744
* @since 1.4.0
3845
*/
3946
@Configuration
40-
@ConditionalOnWebApplication(type = Type.SERVLET)
4147
@EnableConfigurationProperties
4248
public class RestDocsAutoConfiguration {
4349

44-
@Bean
45-
@ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class)
46-
public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer(
47-
ObjectProvider<RestDocsMockMvcConfigurationCustomizer> configurationCustomizerProvider,
48-
RestDocumentationContextProvider contextProvider) {
49-
MockMvcRestDocumentationConfigurer configurer = MockMvcRestDocumentation
50-
.documentationConfiguration(contextProvider);
51-
RestDocsMockMvcConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider
52-
.getIfAvailable();
53-
if (configurationCustomizer != null) {
54-
configurationCustomizer.customize(configurer);
50+
@Configuration
51+
@ConditionalOnWebApplication(type = Type.SERVLET)
52+
@ConditionalOnClass(MockMvcRestDocumentation.class)
53+
static class RestDocsMockMvcAutoConfiguration {
54+
55+
@Bean
56+
@ConditionalOnMissingBean(MockMvcRestDocumentationConfigurer.class)
57+
public MockMvcRestDocumentationConfigurer restDocsMockMvcConfigurer(
58+
ObjectProvider<RestDocsMockMvcConfigurationCustomizer> configurationCustomizerProvider,
59+
RestDocumentationContextProvider contextProvider) {
60+
MockMvcRestDocumentationConfigurer configurer = MockMvcRestDocumentation
61+
.documentationConfiguration(contextProvider);
62+
RestDocsMockMvcConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider
63+
.getIfAvailable();
64+
if (configurationCustomizer != null) {
65+
configurationCustomizer.customize(configurer);
66+
}
67+
return configurer;
68+
}
69+
70+
@Bean
71+
@ConfigurationProperties(prefix = "spring.test.restdocs")
72+
public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer(
73+
MockMvcRestDocumentationConfigurer configurer,
74+
ObjectProvider<RestDocumentationResultHandler> resultHandler) {
75+
return new RestDocsMockMvcBuilderCustomizer(configurer,
76+
resultHandler.getIfAvailable());
5577
}
56-
return configurer;
78+
5779
}
5880

59-
@Bean
60-
@ConfigurationProperties(prefix = "spring.test.restdocs")
61-
public RestDocsMockMvcBuilderCustomizer restDocumentationConfigurer(
62-
MockMvcRestDocumentationConfigurer configurer,
63-
ObjectProvider<RestDocumentationResultHandler> resultHandler) {
64-
return new RestDocsMockMvcBuilderCustomizer(configurer,
65-
resultHandler.getIfAvailable());
81+
@Configuration
82+
@ConditionalOnClass({ RequestSpecification.class, RestAssuredRestDocumentation.class })
83+
static class RestDocsRestAssuredAutoConfiguration {
84+
85+
@Bean
86+
@ConditionalOnMissingBean(RequestSpecification.class)
87+
public RequestSpecification restDocsRestAssuredConfigurer(
88+
ObjectProvider<RestDocsRestAssuredConfigurationCustomizer> configurationCustomizerProvider,
89+
RestDocumentationContextProvider contextProvider) {
90+
RestAssuredRestDocumentationConfigurer configurer = RestAssuredRestDocumentation
91+
.documentationConfiguration(contextProvider);
92+
RestDocsRestAssuredConfigurationCustomizer configurationCustomizer = configurationCustomizerProvider.getIfAvailable();
93+
if (configurationCustomizer != null) {
94+
configurationCustomizer.customize(configurer);
95+
}
96+
return new RequestSpecBuilder().addFilter(configurer).build();
97+
}
98+
99+
@Bean
100+
@ConfigurationProperties(prefix = "spring.test.restdocs")
101+
public RestDocsRestAssuredBuilderCustomizer restAssuredBuilderCustomizer(
102+
RequestSpecification configurer) {
103+
return new RestDocsRestAssuredBuilderCustomizer(configurer);
104+
}
105+
66106
}
67107

108+
68109
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.restdocs;
18+
19+
import io.restassured.specification.RequestSpecification;
20+
21+
import org.springframework.beans.factory.InitializingBean;
22+
import org.springframework.util.StringUtils;
23+
24+
/**
25+
* A customizer that configures Spring REST Docs with RestAssured.
26+
*
27+
* @author Eddú Meléndez
28+
*/
29+
class RestDocsRestAssuredBuilderCustomizer implements InitializingBean {
30+
31+
private final RequestSpecification delegate;
32+
33+
private String uriScheme;
34+
35+
private String uriHost;
36+
37+
private Integer uriPort;
38+
39+
RestDocsRestAssuredBuilderCustomizer(RequestSpecification delegate) {
40+
this.delegate = delegate;
41+
}
42+
43+
public String getUriScheme() {
44+
return this.uriScheme;
45+
}
46+
47+
public void setUriScheme(String uriScheme) {
48+
this.uriScheme = uriScheme;
49+
}
50+
51+
public String getUriHost() {
52+
return this.uriHost;
53+
}
54+
55+
public void setUriHost(String uriHost) {
56+
this.uriHost = uriHost;
57+
}
58+
59+
public Integer getUriPort() {
60+
return this.uriPort;
61+
}
62+
63+
public void setUriPort(Integer uriPort) {
64+
this.uriPort = uriPort;
65+
}
66+
67+
@Override
68+
public void afterPropertiesSet() throws Exception {
69+
if (StringUtils.hasText(this.uriScheme) && StringUtils.hasText(this.uriHost)) {
70+
this.delegate.baseUri(this.uriScheme + "://" + this.uriHost);
71+
}
72+
if (this.uriPort != null) {
73+
this.delegate.port(this.uriPort);
74+
}
75+
}
76+
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.restdocs;
18+
19+
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
20+
21+
/**
22+
* A customizer for {@link RestAssuredRestDocumentationConfigurer}. If a
23+
* {@code RestDocsRestAssuredConfigurationCustomizer} bean is found in the application
24+
* context it will be {@link #customize called} to customize the
25+
* {@code RestAssuredRestDocumentationConfigurer} before it is applied. Intended for use
26+
* only when the attributes on {@link AutoConfigureRestDocs} do not provide sufficient
27+
* customization.
28+
*
29+
* @author Eddú Meléndez
30+
* @since 2.0.0
31+
*/
32+
@FunctionalInterface
33+
public interface RestDocsRestAssuredConfigurationCustomizer {
34+
35+
/**
36+
* Customize the given {@code configurer}.
37+
* @param configurer the configurer
38+
*/
39+
void customize(RestAssuredRestDocumentationConfigurer configurer);
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2012-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.test.autoconfigure.restdocs;
18+
19+
import java.io.File;
20+
21+
import io.restassured.specification.RequestSpecification;
22+
import org.assertj.core.api.Condition;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
27+
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.boot.test.context.SpringBootTest;
29+
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
30+
import org.springframework.boot.test.context.TestConfiguration;
31+
import org.springframework.boot.web.server.LocalServerPort;
32+
import org.springframework.restdocs.restassured3.RestAssuredRestDocumentationConfigurer;
33+
import org.springframework.restdocs.templates.TemplateFormats;
34+
import org.springframework.test.context.junit4.SpringRunner;
35+
import org.springframework.util.FileSystemUtils;
36+
37+
import static io.restassured.RestAssured.given;
38+
import static org.assertj.core.api.Assertions.assertThat;
39+
import static org.hamcrest.CoreMatchers.is;
40+
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
41+
import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document;
42+
import static org.springframework.restdocs.restassured3.operation.preprocess.RestAssuredPreprocessors.modifyUris;
43+
44+
/**
45+
* Tests for {@link AutoConfigureRestDocs}.
46+
*
47+
* @author Eddú Meléndez
48+
*/
49+
@RunWith(SpringRunner.class)
50+
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
51+
@AutoConfigureRestDocs
52+
public class RestAssuredAutoConfigurationAdvancedConfigurationIntegrationTests {
53+
54+
@LocalServerPort
55+
private int port;
56+
57+
@Before
58+
public void deleteSnippets() {
59+
FileSystemUtils.deleteRecursively(new File("target/generated-snippets"));
60+
}
61+
62+
@Autowired
63+
private RequestSpecification documentationSpec;
64+
65+
@Test
66+
public void snippetGeneration() throws Exception {
67+
given(this.documentationSpec)
68+
.filter(document("default-snippets", preprocessRequest(modifyUris()
69+
.scheme("https").host("api.example.com").removePort()))).when()
70+
.port(this.port).get("/").then().assertThat().statusCode(is(200));
71+
File defaultSnippetsDir = new File("target/generated-snippets/default-snippets");
72+
assertThat(defaultSnippetsDir).exists();
73+
assertThat(new File(defaultSnippetsDir, "curl-request.md")).has(
74+
contentContaining("'https://api.example.com/'"));
75+
assertThat(new File(defaultSnippetsDir, "http-request.md")).has(
76+
contentContaining("api.example.com"));
77+
assertThat(new File(defaultSnippetsDir, "http-response.md")).isFile();
78+
}
79+
80+
private Condition<File> contentContaining(String toContain) {
81+
return new ContentContainingCondition(toContain);
82+
}
83+
84+
@TestConfiguration
85+
public static class CustomizationConfiguration implements
86+
RestDocsRestAssuredConfigurationCustomizer {
87+
88+
@Override
89+
public void customize(RestAssuredRestDocumentationConfigurer configurer) {
90+
configurer.snippets().withTemplateFormat(TemplateFormats.markdown());
91+
}
92+
93+
}
94+
95+
}

0 commit comments

Comments
 (0)