This commit is contained in:
Marcin Grzejszczak
2017-05-10 10:35:18 +02:00
4 changed files with 63 additions and 14 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<String, Object> model = new HashMap<>();
private static final Set<String> 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<String, Object> 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<String, String> 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<String, String> 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<String, Object> createModelForContract(Operation operation) {

View File

@@ -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<String> headerNames(Set<Header> headers) {
Set<String> 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);
}