diff --git a/README.adoc b/README.adoc index 2fef004505..0bb85b9e19 100644 --- a/README.adoc +++ b/README.adoc @@ -1703,8 +1703,9 @@ will generate a stub something like this: NOTE: You can use either the `wiremock()` method or the `jsonPath()` and `contentType()` methods to create request matchers, but not both. -On the consumer side, assuming the `resource.json` generated above is -available on the classpath, you can create a stub using WireMock in a +On the consumer side, you can make the `resource.json` generated above +available on the classpath (by https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_publishing_stubs_as_jars[publishing stubs as JARs] for example). +After that, you can create a stub using WireMock in a number of different ways, including as described above using `@AutoConfigureWireMock(stubs="classpath:resource.json")`. @@ -1714,6 +1715,12 @@ Another thing that can be generated with Spring RestDocs is the Spring Cloud Contract DSL file and documentation. If you combine that with Spring Cloud WireMock then you're getting both the contracts and stubs. +Why would you want to use this feature? Some people in the community asked questions +about situation in which they would like to move to DSL based contract definition +but they already have a lot of Spring MVC tests. Using this feature allows you to generate +the contract files that you can later modify and move to proper folders so that the +plugin picks them up. + TIP: You might wonder why this functionality is in the WireMock module. Come to think of it, it does make sense since it makes little sense to generate only contracts and not generate the stubs. That's why we suggest to do both. @@ -1750,15 +1757,13 @@ import org.springframework.cloud.contract.spec.Contract Contract.make { request { method 'POST' - url 'http://localhost:8080/foo' + url '/foo' body(''' {"foo": 23 } ''') headers { header('''Accept''', '''application/json''') header('''Content-Type''', '''application/json''') - header('''Host''', '''localhost:8080''') - header('''Content-Length''', '''12''') } } response { diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc index 7ee2507e4c..1218474f22 100644 --- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc +++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc @@ -300,6 +300,12 @@ Another thing that can be generated with Spring RestDocs is the Spring Cloud Contract DSL file and documentation. If you combine that with Spring Cloud WireMock then you're getting both the contracts and stubs. +Why would you want to use this feature? Some people in the community asked questions +about situation in which they would like to move to DSL based contract definition +but they already have a lot of Spring MVC tests. Using this feature allows you to generate +the contract files that you can later modify and move to proper folders so that the +plugin picks them up. + TIP: You might wonder why this functionality is in the WireMock module. Come to think of it, it does make sense since it makes little sense to generate only contracts and not generate the stubs. That's why we suggest to do both. @@ -323,15 +329,13 @@ import org.springframework.cloud.contract.spec.Contract Contract.make { request { method 'POST' - url 'http://localhost:8080/foo' + url '/foo' body(''' {"foo": 23 } ''') headers { header('''Accept''', '''application/json''') header('''Content-Type''', '''application/json''') - header('''Host''', '''localhost:8080''') - header('''Content-Length''', '''12''') } } response { diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippet.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippet.java index 37073c5d18..16289f8b31 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippet.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippet.java @@ -5,6 +5,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; +import java.net.URI; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -17,6 +19,7 @@ import org.springframework.restdocs.operation.OperationRequest; import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.snippet.TemplatedSnippet; import org.springframework.restdocs.templates.TemplateEngine; +import org.springframework.util.StringUtils; /** * A {@link org.springframework.restdocs.snippet.Snippet} that documents the Spring Cloud Contract Groovy DSL. @@ -30,6 +33,8 @@ public class ContractDslSnippet extends TemplatedSnippet { private static final String SNIPPET_NAME = "dsl-contract"; private Map model = new HashMap<>(); + private static final Set IGNORED_HEADERS = + new HashSet<>(Arrays.asList(HttpHeaders.HOST, HttpHeaders.CONTENT_LENGTH)); /** * Creates a new {@code ContractDslSnippet} with no additional attributes. @@ -53,7 +58,8 @@ public class ContractDslSnippet extends TemplatedSnippet { return this.model; } - @Override public void document(Operation operation) throws IOException { + @Override + public void document(Operation operation) throws IOException { TemplateEngine templateEngine = (TemplateEngine) operation.getAttributes().get(TemplateEngine.class.getName()); String renderedContract = templateEngine.compileTemplate("default-dsl-contract-only") .render(createModelForContract(operation)); @@ -90,12 +96,30 @@ public class ContractDslSnippet extends TemplatedSnippet { private void insertRequestModel(Operation operation, Map model) { OperationRequest request = operation.getRequest(); model.put("request_method", request.getMethod()); - model.put("request_url", request.getUri()); + model.put("request_url", prepareRequestUrl(request.getUri())); model.put("request_body_present", request.getContent().length > 0); model.put("request_body", request.getContentAsString()); - HttpHeaders headers = request.getHeaders(); + Map headers = request.getHeaders().toSingleValueMap(); + filterHeaders(headers); model.put("request_headers_present", !headers.isEmpty()); - model.put("request_headers", headers.toSingleValueMap().entrySet()); + model.put("request_headers", headers.entrySet()); + } + + private void filterHeaders(Map headers) { + for (String header : IGNORED_HEADERS) { + if (headers.containsKey(header)) { + headers.remove(header); + } + } + } + + private String prepareRequestUrl(URI uri) { + String path = uri.getRawPath(); + String query = uri.getRawQuery(); + if (StringUtils.hasText(query)) { + path = path + "?" + query; + } + return path; } private Map createModelForContract(Operation operation) { diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java index f38b1d38dc..0006f6c5b5 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java @@ -5,6 +5,8 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.HashSet; +import java.util.Set; import org.junit.Before; import org.junit.Rule; @@ -14,8 +16,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.contract.spec.Contract; +import org.springframework.cloud.contract.spec.internal.Header; import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.restdocs.JUnitRestDocumentation; import org.springframework.test.context.junit4.SpringRunner; @@ -80,9 +84,12 @@ public class ContractDslSnippetTests { String contract = readFromFile(file("/contracts/index.groovy")); // try to parse the contract Contract parsedContract = ContractVerifierDslConverter.convert(contract); - then(parsedContract.getRequest().getHeaders().getEntries()).isNotEmpty(); + then(parsedContract.getRequest().getHeaders().getEntries()).isNotNull(); + then(headerNames(parsedContract.getRequest().getHeaders().getEntries())).doesNotContain + (HttpHeaders.HOST, HttpHeaders.CONTENT_LENGTH); then(parsedContract.getRequest().getMethod().getClientValue()).isNotNull(); then(parsedContract.getRequest().getUrl().getClientValue()).isNotNull(); + then(parsedContract.getRequest().getUrl().getClientValue().toString()).startsWith("/"); then(parsedContract.getRequest().getBody().getClientValue()).isNotNull(); then(parsedContract.getResponse().getStatus().getClientValue()).isNotNull(); then(parsedContract.getResponse().getHeaders().getEntries()).isNotEmpty(); @@ -101,9 +108,10 @@ public class ContractDslSnippetTests { String contract = readFromFile(file("/contracts/empty.groovy")); // try to parse the contract Contract parsedContract = ContractVerifierDslConverter.convert(contract); - then(parsedContract.getRequest().getHeaders().getEntries()).isNotEmpty(); + then(parsedContract.getRequest().getHeaders()).isNull(); then(parsedContract.getRequest().getMethod().getClientValue()).isNotNull(); then(parsedContract.getRequest().getUrl().getClientValue()).isNotNull(); + then(parsedContract.getRequest().getUrl().getClientValue().toString()).startsWith("/"); then(parsedContract.getRequest().getBody()).isNull(); then(parsedContract.getResponse().getStatus().getClientValue()).isNotNull(); then(parsedContract.getResponse().getHeaders()).isNull(); @@ -111,6 +119,14 @@ public class ContractDslSnippetTests { then(parsedContract.getResponse().getMatchers()).isNull(); } + private Set headerNames(Set
headers) { + Set names = new HashSet<>(); + for (Header header : headers) { + names.add(header.getName()); + } + return names; + } + private File file(String name) throws URISyntaxException { return new File(OUTPUT, name); }