diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc
index fbdd7f9cab..a3a89f8fd9 100644
--- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc
@@ -157,6 +157,29 @@ is saying: any valid POST with an "id" field will get back an the same
response as in this test. You can chain together calls to
`.jsonPath()` to add additional matchers.
+Instead of the `jsonPath` and `contentType` convenience methods, you
+can also use WireMock to verify the request matches the created
+stub. Example:
+
+[source,java,indent=0]
+----
+ @Test
+ public void contextLoads() throws Exception {
+ mockMvc.perform(post("/resource")
+ .content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
+ .andExpect(status.isOk())
+ .andDo(verify()
+ .wiremock(WireMock.post(
+ urlPathEquals("/resource"))
+ .withRequestBody(matchingJsonPath("$.id"))
+ .stub("resource"));
+ }
+----
+
+The WireMock API is rich - you can match headers, query parameters,
+and request body by regex for instance - so this can useful to create
+stubs with a wider range of parameters.
+
On the consumer side, assuming the `resource.json` generated above is
available on the classpath, you can create a stub using WireMock in a
number of different ways, including as described above using
diff --git a/samples/standalone/restdocs/http-client/build.gradle b/samples/standalone/restdocs/http-client/build.gradle
index 4db1443b15..23424e685a 100644
--- a/samples/standalone/restdocs/http-client/build.gradle
+++ b/samples/standalone/restdocs/http-client/build.gradle
@@ -5,9 +5,6 @@ buildscript {
maven { url "http://repo.spring.io/libs-snapshot-local" }
maven { url "http://repo.spring.io/libs-release-local" }
maven { url "http://repo.spring.io/libs-staging-local" }
- maven { url 'http://repo.spring.io/plugins-snapshot' }
- maven { url "http://repo.spring.io/plugins-release-local" }
- maven { url "http://repo.spring.io/plugins-staging-local/" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:1.4.0.BUILD-SNAPSHOT"
@@ -41,7 +38,8 @@ dependencies {
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile 'org.springframework.cloud:spring-cloud-contract-wiremock'
- testCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner"
+ testCompile "org.springframework.boot:spring-boot-starter-test"
+ testCompile "com.example:http-server-restdocs:0.0.1-SNAPSHOT:stubs"
}
test {
diff --git a/samples/standalone/restdocs/http-client/pom.xml b/samples/standalone/restdocs/http-client/pom.xml
index 62759e90b3..c4dc7be630 100644
--- a/samples/standalone/restdocs/http-client/pom.xml
+++ b/samples/standalone/restdocs/http-client/pom.xml
@@ -141,22 +141,6 @@
false
-
- spring-plugin-snapshots
- Spring Snapshots
- http://repo.spring.io/plugins-snapshot-local
-
- true
-
-
-
- spring-plugin-milestones
- Spring Milestones
- http://repo.spring.io/plugins-milestone-local
-
- false
-
-
diff --git a/samples/standalone/runTests.sh b/samples/standalone/runTests.sh
index f3fb7755d5..477a9899e8 100755
--- a/samples/standalone/runTests.sh
+++ b/samples/standalone/runTests.sh
@@ -25,6 +25,14 @@ echo -e "\n\nBuilding client (uses Spring Cloud Contract Stub Runner)"
cd dsl/http-client
./gradlew clean build -PverifierVersion=${VERIFIER_VERSION} --stacktrace
cd $ROOT
+echo -e "Building server (uses Spring Cloud Contract Verifier Gradle Plugin)"
+cd restdocs/http-server
+./gradlew clean build publishToMavenLocal -PverifierVersion=${VERIFIER_VERSION} --stacktrace
+cd $ROOT
+echo -e "\n\nBuilding client (uses Spring Cloud Contract Stub Runner)"
+cd restdocs/http-client
+./gradlew clean build -PverifierVersion=${VERIFIER_VERSION} --stacktrace
+cd $ROOT
echo -e "\n\nClearing saved stubs"
rm -rf $LOCAL_MAVEN_REPO/repository/org/springframework/cloud/contract/testprojects/
diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractRequestHandler.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractRequestHandler.java
index 26dc16763a..e6f70b1ea9 100644
--- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractRequestHandler.java
+++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractRequestHandler.java
@@ -18,6 +18,7 @@ package org.springframework.cloud.contract.wiremock.restdocs;
import static org.assertj.core.api.Assertions.assertThat;
+import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedHashMap;
@@ -25,6 +26,7 @@ import java.util.Map;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
@@ -32,6 +34,11 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
+import com.github.tomakehurst.wiremock.client.RemoteMappingBuilder;
+import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
+import com.github.tomakehurst.wiremock.matching.MatchResult;
+import com.github.tomakehurst.wiremock.servlet.WireMockHttpServletRequestAdapter;
+import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.jayway.jsonpath.JsonPath;
public class ContractRequestHandler implements ResultHandler {
@@ -42,6 +49,8 @@ public class ContractRequestHandler implements ResultHandler {
private MediaType contentType;
private String name;
+ private RemoteMappingBuilder, ?> builder;
+
public ContractRequestHandler() {
}
@@ -55,7 +64,7 @@ public class ContractRequestHandler implements ResultHandler {
@Override
public void handle(MvcResult result) throws Exception {
MockHttpServletRequest request = result.getRequest();
- Map configuration = getConfiguration(result);
+ Map configuration = getConfiguration(result);
String actual = StreamUtils.copyToString(request.getInputStream(),
Charset.forName("UTF-8"));
for (JsonPath jsonPath : jsonPaths.values()) {
@@ -69,19 +78,50 @@ public class ContractRequestHandler implements ResultHandler {
assertThat(contentType.includes(MediaType.valueOf(resultType))).isTrue()
.as("content type did not match");
}
+ if (builder != null) {
+ builder.willReturn(getResponseDefinition(result));
+ StubMapping stubMapping = builder.build();
+ MatchResult match = stubMapping.getRequest()
+ .match(new WireMockHttpServletRequestAdapter(request));
+ assertThat(match.isExactMatch()).as("wiremock did not match request").isTrue();
+ configuration.put("contract.stubMapping", stubMapping);
+ }
MockMvcRestDocumentation.document(this.name).handle(result);
}
+ private ResponseDefinitionBuilder getResponseDefinition(MvcResult result)
+ throws UnsupportedEncodingException {
+ MockHttpServletResponse response = result.getResponse();
+ ResponseDefinitionBuilder definition = ResponseDefinitionBuilder
+ .responseDefinition().withBody(response.getContentAsString())
+ .withStatus(response.getStatus());
+ addResponseHeaders(definition, response);
+ return definition;
+ }
+
+ private void addResponseHeaders(ResponseDefinitionBuilder definition,
+ MockHttpServletResponse input) {
+ for (String name : input.getHeaderNames()) {
+ definition.withHeader(name, input.getHeader(name));
+ }
+ }
+
private Map getConfiguration(MvcResult result) {
@SuppressWarnings("unchecked")
- Map map = (Map) result.getRequest().getAttribute(ATTRIBUTE_NAME_CONFIGURATION);
- if (map==null) {
+ Map map = (Map) result.getRequest()
+ .getAttribute(ATTRIBUTE_NAME_CONFIGURATION);
+ if (map == null) {
map = new HashMap<>();
result.getRequest().setAttribute(ATTRIBUTE_NAME_CONFIGURATION, map);
}
return map;
}
+ public ContractRequestHandler wiremock(RemoteMappingBuilder, ?> builder) {
+ this.builder = builder;
+ return this;
+ }
+
public ContractRequestHandler jsonPath(String expression, Object... args) {
compile(expression, args);
return this;
diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerRestDocsMatcherApplicationTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerRestDocsMatcherApplicationTests.java
new file mode 100644
index 0000000000..85f4bcae3f
--- /dev/null
+++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockServerRestDocsMatcherApplicationTests.java
@@ -0,0 +1,78 @@
+package org.springframework.cloud.contract.wiremock;
+
+import org.junit.ComparisonFailure;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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.WiremockServerRestDocsMatcherApplicationTests.TestConfiguration;
+import org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.test.annotation.DirtiesContext;
+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.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = TestConfiguration.class)
+@AutoConfigureRestDocs(outputDir = "target/snippets")
+@AutoConfigureMockMvc
+@DirtiesContext
+public class WiremockServerRestDocsMatcherApplicationTests {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Rule
+ public ExpectedException expected = ExpectedException.none();
+
+ @Test
+ public void matchesRequest() throws Exception {
+ mockMvc.perform(MockMvcRequestBuilders.post("/resource").content("greeting")
+ .contentType(MediaType.TEXT_PLAIN))
+ .andExpect(MockMvcResultMatchers.content().string("Hello World"))
+ .andDo(WireMockRestDocs.verify()
+ .wiremock(WireMock.post(WireMock.urlPathEqualTo("/resource"))
+ .withRequestBody(WireMock.matching("greeting.*")))
+ .stub("posted"));
+ }
+
+ @Test
+ public void doesNotMatch() throws Exception {
+ expected.expect(ComparisonFailure.class);
+ expected.expectMessage("wiremock did not match");
+ mockMvc.perform(MockMvcRequestBuilders.post("/resource").content("greeting")
+ .contentType(MediaType.TEXT_PLAIN))
+ .andExpect(MockMvcResultMatchers.content().string("Hello World"))
+ .andDo(WireMockRestDocs.verify()
+ .wiremock(WireMock.post(WireMock.urlPathEqualTo("/resource"))
+ .withRequestBody(WireMock.matching("garbage.*")))
+ .stub("posted"));
+ }
+
+ @Configuration
+ @RestController
+ protected static class TestConfiguration {
+
+ @ResponseBody
+ @RequestMapping(value = "/resource", method = RequestMethod.POST)
+ public String resource(@RequestBody String body) {
+ return "Hello World";
+ }
+
+ }
+
+}