Add support for placeholders in Wiremock stubs (#1109)
* Generate wiremock stubs in restdocs with placeholders for port * Add support for placeholders in Wiremock stubs * Ensure response-template transformer is added if needed fixes gh-1088
This commit is contained in:
committed by
Marcin Grzejszczak
parent
31f43648ac
commit
33c8cbfc87
@@ -24,24 +24,6 @@
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
@@ -85,6 +67,24 @@
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.tomakehurst</groupId>
|
||||
<artifactId>wiremock-jre8-standalone</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.common.Slf4jNotifier;
|
||||
import com.github.tomakehurst.wiremock.core.Options;
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -97,6 +98,9 @@ public class WireMockConfiguration implements SmartLifecycle {
|
||||
}
|
||||
registerFiles(factory);
|
||||
factory.notifier(new Slf4jNotifier(true));
|
||||
if (this.wireMock.getPlaceholders().isEnabled()) {
|
||||
factory.extensions(new ResponseTemplateTransformer(false));
|
||||
}
|
||||
this.options = factory;
|
||||
if (this.customizer != null) {
|
||||
this.customizer.customize(factory);
|
||||
@@ -245,6 +249,8 @@ class WireMockProperties {
|
||||
|
||||
private Server server = new Server();
|
||||
|
||||
private Placeholders placeholders = new Placeholders();
|
||||
|
||||
private boolean restTemplateSslEnabled;
|
||||
|
||||
public boolean isRestTemplateSslEnabled() {
|
||||
@@ -263,6 +269,32 @@ class WireMockProperties {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
public Placeholders getPlaceholders() {
|
||||
return this.placeholders;
|
||||
}
|
||||
|
||||
public void setPlaceholders(Placeholders placeholders) {
|
||||
this.placeholders = placeholders;
|
||||
}
|
||||
|
||||
public class Placeholders {
|
||||
|
||||
/**
|
||||
* Flag to indicate that http URLs in generated wiremock stubs should be filtered
|
||||
* to add or resolve a placeholder for a dynamic port.
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Server {
|
||||
|
||||
private int port = 8080;
|
||||
|
||||
@@ -235,12 +235,14 @@ public final class WireMockRestServiceServer {
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMatcher matchContents(final ContentPattern pattern) {
|
||||
private RequestMatcher matchContents(
|
||||
@SuppressWarnings("rawtypes") final ContentPattern pattern) {
|
||||
return new RequestMatcher() {
|
||||
@Override
|
||||
public void match(ClientHttpRequest request)
|
||||
throws IOException, AssertionError {
|
||||
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;
|
||||
@SuppressWarnings("unchecked")
|
||||
MatchResult result = pattern.match(mockRequest.getBodyAsString());
|
||||
MatcherAssert.assertThat(
|
||||
"Request as string [" + mockRequest.getBodyAsString() + "]",
|
||||
|
||||
@@ -19,7 +19,12 @@ package org.springframework.cloud.contract.wiremock.restdocs;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
|
||||
import org.springframework.restdocs.operation.OperationRequest;
|
||||
import org.springframework.restdocs.operation.OperationResponse;
|
||||
import org.springframework.restdocs.operation.OperationResponseFactory;
|
||||
import org.springframework.restdocs.operation.preprocess.OperationPreprocessor;
|
||||
|
||||
/**
|
||||
* Custom configuration for Spring RestDocs that adds a WireMock snippet (for generating
|
||||
@@ -38,9 +43,42 @@ import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
|
||||
public class WireMockRestDocsConfiguration
|
||||
implements RestDocsMockMvcConfigurationCustomizer {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public WireMockRestDocsConfiguration(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(MockMvcRestDocumentationConfigurer configurer) {
|
||||
if (this.environment.getProperty("wiremock.placeholders.enabled", "true")
|
||||
.equals("true")) {
|
||||
configurer.operationPreprocessors()
|
||||
.withResponseDefaults(new ForwardHeaderPreprocessor());
|
||||
}
|
||||
configurer.snippets().withAdditionalDefaults(new WireMockSnippet());
|
||||
}
|
||||
|
||||
static class ForwardHeaderPreprocessor implements OperationPreprocessor {
|
||||
|
||||
private final OperationResponseFactory responseFactory = new OperationResponseFactory();
|
||||
|
||||
@Override
|
||||
public OperationRequest preprocess(OperationRequest request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationResponse preprocess(OperationResponse response) {
|
||||
String content = response.getContentAsString();
|
||||
if (content.contains("localhost:8080")) {
|
||||
content = content.replace("localhost:8080",
|
||||
"localhost:{{request.requestLine.port}}");
|
||||
response = this.responseFactory.createFrom(response, content.getBytes());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,9 +146,14 @@ public class WireMockSnippet implements Snippet {
|
||||
}
|
||||
|
||||
private ResponseDefinitionBuilder response(Operation operation) {
|
||||
return aResponse().withHeaders(responseHeaders(operation))
|
||||
.withBody(operation.getResponse().getContentAsString())
|
||||
.withStatus(operation.getResponse().getStatus().value());
|
||||
String content = operation.getResponse().getContentAsString();
|
||||
ResponseDefinitionBuilder response = aResponse()
|
||||
.withHeaders(responseHeaders(operation)).withBody(content);
|
||||
if (content != null
|
||||
&& content.contains("localhost:{{request.requestLine.port}}")) {
|
||||
response = response.withTransformers("response-template");
|
||||
}
|
||||
return response.withStatus(operation.getResponse().getStatus().value());
|
||||
}
|
||||
|
||||
private MappingBuilder request(Operation operation) {
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -36,9 +37,17 @@ public class AutoConfigureWireMockStubsApplicationTests {
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Value("localhost:${wiremock.server.port}")
|
||||
private String hostname;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
assertThat(this.service.go()).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void link() throws Exception {
|
||||
assertThat(this.service.link()).contains("http://" + this.hostname);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.matching.MultiValuePattern;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
@@ -35,6 +37,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.contract.wiremock.WiremockServerRestDocsApplicationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
@@ -43,7 +46,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
@@ -93,6 +99,24 @@ public class WiremockServerRestDocsApplicationTests {
|
||||
.isEqualTo(WireMock.equalTo("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stubsRenderLinksWithPlaceholder() throws Exception {
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/link"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("link:")))
|
||||
.andDo(document("link"));
|
||||
|
||||
File file = new File("target/snippets/stubs", "link.json");
|
||||
BDDAssertions.then(file).exists();
|
||||
StubMapping stubMapping = StubMapping
|
||||
.buildFrom(new String(Files.readAllBytes(file.toPath())));
|
||||
String body = stubMapping.getResponse().getBody();
|
||||
BDDAssertions.then(body)
|
||||
.contains("http://localhost:{{request.requestLine.port}}/link");
|
||||
BDDAssertions.then(stubMapping.getResponse().getTransformers())
|
||||
.contains("response-template");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@RestController
|
||||
protected static class TestConfiguration {
|
||||
@@ -103,6 +127,14 @@ public class WiremockServerRestDocsApplicationTests {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("/link")
|
||||
public String link(HttpServletRequest request) {
|
||||
UriComponents uriComponents = UriComponentsBuilder
|
||||
.fromHttpRequest(new ServletServerHttpRequest(request)).build();
|
||||
return "link: " + uriComponents.toUriString();
|
||||
}
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("/status")
|
||||
public ResponseEntity<String> status() {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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.cloud.contract.wiremock;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.contract.wiremock.WiremockServerRestDocsHypermediaApplicationTests.TestConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TestConfiguration.class,
|
||||
properties = "wiremock.placeholders.enabled=false")
|
||||
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
@AutoConfigureMockMvc
|
||||
public class WiremockServerRestDocsHypermediaApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void stubsRenderLinksWithoutPlaceholder() throws Exception {
|
||||
this.mockMvc.perform(MockMvcRequestBuilders.get("/link"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(containsString("link:")))
|
||||
.andDo(document("link"));
|
||||
|
||||
File file = new File("target/snippets/stubs", "link.json");
|
||||
BDDAssertions.then(file).exists();
|
||||
StubMapping stubMapping = StubMapping
|
||||
.buildFrom(new String(Files.readAllBytes(file.toPath())));
|
||||
String body = stubMapping.getResponse().getBody();
|
||||
BDDAssertions.then(body).contains("http://localhost:8080/link");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@RestController
|
||||
protected static class TestConfiguration {
|
||||
|
||||
@ResponseBody
|
||||
@RequestMapping("/link")
|
||||
public String resource(HttpServletRequest request) {
|
||||
UriComponents uriComponents = UriComponentsBuilder
|
||||
.fromHttpRequest(new ServletServerHttpRequest(request)).build();
|
||||
return "link: " + uriComponents.toUriString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -87,6 +87,12 @@ class Service {
|
||||
return this.restTemplate.getForEntity(requestUrl, String.class).getBody();
|
||||
}
|
||||
|
||||
public String link() {
|
||||
String requestUrl = this.base + "/link";
|
||||
log.info("Will send a request to [" + requestUrl + "]");
|
||||
return this.restTemplate.getForEntity(requestUrl, String.class).getBody();
|
||||
}
|
||||
|
||||
public String pom() {
|
||||
String requestUrl = this.base + "/pom.xml";
|
||||
log.info("Will send a request to [" + requestUrl + "]");
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"priority": 1,
|
||||
"request": {
|
||||
"urlPath": "/link",
|
||||
"method": "GET"
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"body": "{\"_links\": {\"markup\":{\"href\":\"http://localhost:{{request.requestLine.port}}/documents\"}}}",
|
||||
"transformers": ["response-template"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user