Add some docs for restdocs usage and rename some things
This commit is contained in:
@@ -86,4 +86,78 @@ Currently we support Tomcat, Jetty and Undertow as Spring Boot
|
||||
embedded servers, and Wiremock itself has "native" support for a
|
||||
particular version of Jetty (currently 9.2). To use the native Jetty
|
||||
you need to add the native wiremock dependencies and exclude the
|
||||
Spring Boot container if there is one.
|
||||
Spring Boot container if there is one.
|
||||
|
||||
== Generating Stubs using RestDocs
|
||||
|
||||
https://projects.spring.io/spring-restdocs[Spring RestDocs] can be
|
||||
used to generate documentation (e.g. in asciidoctor format) for an
|
||||
HTTP API with Spring MockMvc or RestEasy. At the same time as you
|
||||
generate documentation for your API, you can also generate WireMock
|
||||
stubs, by using Spring Cloud Contract WireMock. Just write your normal
|
||||
RestDocs test cases and use `@AutoConfigureRestDocs` to have stubs
|
||||
automatically in the restdocs output directory. For example:
|
||||
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
@AutoConfigureMockMvc
|
||||
public class ApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
mockMvc.perform(get("/resource"))
|
||||
.andExpect(content().string("Hello World"))
|
||||
.andDo(document("resource"));
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
From this test will be generated a WireMock stub at
|
||||
"target/snippets/stubs/resource.json". It matches all GET requests to
|
||||
the "/resource" path.
|
||||
|
||||
To create stubs for PUT and POST it is useful to be able to match the
|
||||
body of the request as well. The main entry point for this is
|
||||
`WireMockRestDocs.verify()` which can be used as a substitute for the
|
||||
`document()` convenience method. For example:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
@AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
@AutoConfigureMockMvc
|
||||
public class ApplicationTests {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
mockMvc.perform(post("/resource")
|
||||
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
|
||||
.andExpect(status.isOk())
|
||||
.andDo(verify().jsonPath("$.id")
|
||||
.stub("resource"));
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
The `jsonPath()` method does 2 things: 1) asserts that the request
|
||||
body in the test itself actually contains the JSON it matches, and 2)
|
||||
makes a WireMock request matcher using the same JSON. So this contract
|
||||
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.
|
||||
|
||||
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
|
||||
`@AutoConfigureWireMock(stubs="classpath:resource.json")`.
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.example.fraud;
|
||||
|
||||
import static org.springframework.cloud.contract.wiremock.restdocs.RestDocsContracts.verify;
|
||||
import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -47,7 +47,7 @@ public class StubGeneratorTests {
|
||||
.andDo(verify().jsonPath("$.clientId")
|
||||
.jsonPath("$[?(@.loanAmount > 1000)]")
|
||||
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
|
||||
.contract("markClientAsFraud"));
|
||||
.stub("markClientAsFraud"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,7 +63,7 @@ public class StubGeneratorTests {
|
||||
.andDo(verify().jsonPath("$.clientId")
|
||||
.jsonPath("$[?(@.loanAmount <= 1000)]")
|
||||
.contentType(MediaType.valueOf("application/vnd.fraud.v1+json"))
|
||||
.contract("markClientAsNotFraud"));
|
||||
.stub("markClientAsNotFraud"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,7 +25,14 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.Options;
|
||||
|
||||
/**
|
||||
* Annotation for test classes that want to start a WireMock server as part of the Spring
|
||||
* Application Context. The port, https port and stub locations (if any) can all be
|
||||
* controlled directly here. For more fine-grained control of the server instance add a
|
||||
* bean of type {@link Options} to the application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@@ -35,11 +42,11 @@ import org.springframework.context.annotation.Import;
|
||||
@Import(WireMockConfiguration.class)
|
||||
@PropertyMapping("wiremock.server")
|
||||
public @interface AutoConfigureWireMock {
|
||||
|
||||
|
||||
int port() default 8080;
|
||||
|
||||
int httpsPort() default -1;
|
||||
|
||||
|
||||
String stubs() default "";
|
||||
|
||||
}
|
||||
|
||||
@@ -39,6 +39,13 @@ import com.github.tomakehurst.wiremock.core.Options;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
|
||||
/**
|
||||
* Configuration and lifecycle for a Spring Application context that wants to run a
|
||||
* WireMock server. Can be used by adding
|
||||
* {@link AutoConfigureWireMock @AutoConfigureWireMock} to a Spring Boot JUnit test. To
|
||||
* configure the properties of the wiremock server you can use the AutoConfigureWireMock
|
||||
* annotation, or add a bean of type {@link Options} (via
|
||||
* {@link WireMockSpring#options()}) to your test context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,10 @@ import static org.springframework.test.web.client.match.MockRestRequestMatchers.
|
||||
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
|
||||
|
||||
/**
|
||||
* Convenience class for loading WireMock stubs into a {@link MockRestServiceServer}. In
|
||||
* this way using a {@link RestTemplate} can mock the responses from a server using
|
||||
* WireMock JSON DSL instead of the native Java DSL.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@@ -42,7 +46,7 @@ public class WireMockRestServiceServer {
|
||||
private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private String suffix = ".json";
|
||||
|
||||
|
||||
private String baseUrl = "";
|
||||
|
||||
private MockRestServiceServer server;
|
||||
@@ -64,7 +68,8 @@ public class WireMockRestServiceServer {
|
||||
public MockRestServiceServer stubs(String... locations) {
|
||||
for (String location : locations) {
|
||||
try {
|
||||
if (!StringUtils.getFilename(location).contains(".") && !location.contains("*")) {
|
||||
if (!StringUtils.getFilename(location).contains(".")
|
||||
&& !location.contains("*")) {
|
||||
if (!location.endsWith("/")) {
|
||||
location = location + "/";
|
||||
}
|
||||
@@ -72,14 +77,19 @@ public class WireMockRestServiceServer {
|
||||
}
|
||||
for (Resource resource : this.resolver.getResources(location)) {
|
||||
StubMapping mapping;
|
||||
mapping = Json.read(StreamUtils.copyToString(resource.getInputStream(), Charset.defaultCharset()),
|
||||
StubMapping.class);
|
||||
this.server.expect(requestTo(this.baseUrl + mapping.getRequest().getUrlPath()))
|
||||
.andRespond(withSuccess(mapping.getResponse().getBody(), MediaType.TEXT_PLAIN));
|
||||
mapping = Json
|
||||
.read(StreamUtils.copyToString(resource.getInputStream(),
|
||||
Charset.defaultCharset()), StubMapping.class);
|
||||
this.server
|
||||
.expect(requestTo(
|
||||
this.baseUrl + mapping.getRequest().getUrlPath()))
|
||||
.andRespond(withSuccess(mapping.getResponse().getBody(),
|
||||
MediaType.TEXT_PLAIN));
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot load resources for: " + location, e);
|
||||
throw new IllegalStateException("Cannot load resources for: " + location,
|
||||
e);
|
||||
}
|
||||
}
|
||||
return this.server;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ContractRequestHandler implements ResultHandler {
|
||||
public ContractRequestHandler() {
|
||||
}
|
||||
|
||||
public ResultHandler contract(String name) {
|
||||
public ResultHandler stub(String name) {
|
||||
this.name = name;
|
||||
// TODO: try and get access to the internals of this so we don't need to store
|
||||
// state in the snippet
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.cloud.contract.wiremock.restdocs;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class RestDocsContracts {
|
||||
|
||||
public static ContractRequestHandler verify() {
|
||||
return new ContractRequestHandler();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.cloud.contract.wiremock.restdocs;
|
||||
|
||||
/**
|
||||
* Convenience class for setting up RestDocs to record WireMock stubs. Example usage:
|
||||
*
|
||||
* <pre>
|
||||
* @RunWith(SpringRunner.class)
|
||||
* @SpringBootTest
|
||||
* @AutoConfigureRestDocs(outputDir = "target/snippets")
|
||||
* @AutoConfigureMockMvc
|
||||
* public class WiremockServerRestDocsApplicationTests {
|
||||
*
|
||||
* @Autowired
|
||||
* private MockMvc mockMvc;
|
||||
*
|
||||
* @Test
|
||||
* public void contextLoads() throws Exception {
|
||||
* mockMvc.perform(get("/resource"))
|
||||
* .andExpect(content().string("Hello World"))
|
||||
* .andDo(verify().contract("resource"));
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* which creates a file "target/snippets/stubs/resource.json" matching any GET request to
|
||||
* "/resource". To match POST and PUT, you can also specify the content type using
|
||||
* <code>verify().contentType(...)</code> and JSON content of the body using
|
||||
* <code>verify().jsonPath(...)</code>.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class WireMockRestDocs {
|
||||
|
||||
public static ContractRequestHandler verify() {
|
||||
return new ContractRequestHandler();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,11 +16,20 @@
|
||||
|
||||
package org.springframework.cloud.contract.wiremock.restdocs;
|
||||
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
|
||||
import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer;
|
||||
|
||||
/**
|
||||
* Custom configuration for Spring RestDocs that adds a WireMock snippet (for generating
|
||||
* JSON stubs). Applied automatically if you use
|
||||
* {@link AutoConfigureRestDocs @AutoConfigureRestDocs} in your test case and this class
|
||||
* is available. JSON stubs are generated and added to the restdocs path under "stubs".
|
||||
*
|
||||
* @see WireMockRestDocs for a convenient entry point for customizing and asserting the
|
||||
* stub behaviour
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user