From 1337bb0deaf8843a7d9c0efa139775e8f80ca55c Mon Sep 17 00:00:00 2001 From: Patrice Conil Date: Wed, 1 Feb 2017 10:13:24 +0100 Subject: [PATCH] Implement JSON matching with JSON Content-Type (#208) fixes #116 --- .../wiremock/restdocs/WireMockSnippet.java | 21 ++- .../restdocs/WireMockSnippetTests.java | 123 +++++++++++++++--- 2 files changed, 124 insertions(+), 20 deletions(-) diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java index a8b405e90f..f509eb7830 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java @@ -43,6 +43,7 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; @@ -63,6 +64,8 @@ public class WireMockSnippet implements Snippet { private StubMapping stubMapping; + private boolean hasJsonBodyRequestToMatch = false; + @Override public void document(Operation operation) throws IOException { extractMatchers(operation); @@ -81,7 +84,8 @@ public class WireMockSnippet implements Snippet { } private void extractMatchers(Operation operation) { - this.stubMapping = (StubMapping) operation.getAttributes().get("contract.stubMapping"); + this.stubMapping = (StubMapping) operation.getAttributes() + .get("contract.stubMapping"); if (this.stubMapping != null) { return; } @@ -89,7 +93,17 @@ public class WireMockSnippet implements Snippet { Set jsonPaths = (Set) operation.getAttributes() .get("contract.jsonPaths"); this.jsonPaths = jsonPaths; - this.contentType = (MediaType) operation.getAttributes().get("contract.contentType"); + this.contentType = (MediaType) operation.getAttributes() + .get("contract.contentType"); + if (this.contentType == null) { + this.hasJsonBodyRequestToMatch = hasJsonContentType(operation); + } + } + + private boolean hasJsonContentType(Operation operation) { + return operation.getRequest().getHeaders().getContentType() != null + && (operation.getRequest().getHeaders().getContentType() + .isCompatibleWith(MediaType.APPLICATION_JSON)); } private ResponseDefinitionBuilder response(Operation operation) { @@ -144,6 +158,9 @@ public class WireMockSnippet implements Snippet { builder.withRequestBody(matchingJsonPath(jsonPath)); } } + else if (this.hasJsonBodyRequestToMatch) { + builder.withRequestBody(equalToJson(content)); + } else { builder.withRequestBody(equalTo(content)); } diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java index fe9d28c7e6..2174ead066 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java @@ -3,6 +3,7 @@ package org.springframework.cloud.contract.wiremock.restdocs; import java.io.File; import java.io.IOException; import java.net.URI; +import java.nio.charset.Charset; import java.nio.file.Files; import java.util.Collection; @@ -17,6 +18,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.restdocs.RestDocumentationContext; import org.springframework.restdocs.operation.Operation; import org.springframework.restdocs.operation.OperationRequest; @@ -24,6 +26,7 @@ import org.springframework.restdocs.operation.OperationRequestPart; import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.operation.Parameters; +import com.github.tomakehurst.wiremock.matching.EqualToJsonPattern; import com.github.tomakehurst.wiremock.stubbing.StubMapping; import static org.assertj.core.api.Assertions.assertThat; @@ -36,9 +39,11 @@ import static org.mockito.Matchers.anyString; @RunWith(MockitoJUnitRunner.class) public class WireMockSnippetTests { - @Mock(answer = Answers.RETURNS_DEEP_STUBS) Operation operation; - @Rule public TemporaryFolder tmp = new TemporaryFolder(); - File outputFolder; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + Operation operation; + @Rule + public TemporaryFolder tmp = new TemporaryFolder(); + private File outputFolder; @Before public void setup() throws IOException { @@ -46,13 +51,15 @@ public class WireMockSnippetTests { } @Test - public void should_maintain_the_response_status_when_generating_stub() throws Exception { + public void should_maintain_the_response_status_when_generating_stub() + throws Exception { WireMockSnippet snippet = new WireMockSnippet(); RestDocumentationContext context = new RestDocumentationContext(this.getClass(), "method", this.outputFolder); given(this.operation.getName()).willReturn("foo"); given(this.operation.getAttributes().get(anyString())).willReturn(null); - given(this.operation.getAttributes().get(RestDocumentationContext.class.getName())).willReturn(context); + given(this.operation.getAttributes() + .get(RestDocumentationContext.class.getName())).willReturn(context); given(this.operation.getRequest()).willReturn(request()); given(this.operation.getResponse()).willReturn(response()); @@ -60,26 +67,57 @@ public class WireMockSnippetTests { File stub = new File(this.outputFolder, "stubs/foo.json"); assertThat(stub).exists(); - StubMapping stubMapping = StubMapping.buildFrom(new String(Files.readAllBytes(stub.toPath()))); - assertThat(stubMapping.getResponse().getStatus()).isEqualTo(HttpStatus.ACCEPTED.value()); + StubMapping stubMapping = StubMapping + .buildFrom(new String(Files.readAllBytes(stub.toPath()))); + assertThat(stubMapping.getResponse().getStatus()) + .isEqualTo(HttpStatus.ACCEPTED.value()); + } + + @Test + public void should_use_equal_to_json_pattern_for_body_when_request_content_type_is_json_when_generating_stub() + throws Exception { + WireMockSnippet snippet = new WireMockSnippet(); + RestDocumentationContext context = new RestDocumentationContext(this.getClass(), + "method", this.outputFolder); + given(this.operation.getName()).willReturn("foo"); + given(this.operation.getAttributes().get(anyString())).willReturn(null); + given(this.operation.getAttributes() + .get(RestDocumentationContext.class.getName())).willReturn(context); + given(this.operation.getRequest()).willReturn(requestPostWithJsonContentType()); + given(this.operation.getResponse()).willReturn(response()); + + snippet.document(this.operation); + + File stub = new File(this.outputFolder, "stubs/foo.json"); + assertThat(stub).exists(); + StubMapping stubMapping = StubMapping + .buildFrom(new String(Files.readAllBytes(stub.toPath()))); + assertThat(stubMapping.getRequest().getBodyPatterns().get(0)) + .isInstanceOf(EqualToJsonPattern.class); + assertThat(stubMapping.getRequest().getBodyPatterns().get(0).getValue()) + .isEqualTo("{\"name\": \"12\"}"); } private OperationResponse response() { return new OperationResponse() { - @Override public HttpStatus getStatus() { + @Override + public HttpStatus getStatus() { return HttpStatus.ACCEPTED; } - @Override public HttpHeaders getHeaders() { + @Override + public HttpHeaders getHeaders() { return new HttpHeaders(); } - @Override public byte[] getContent() { + @Override + public byte[] getContent() { return new byte[0]; } - @Override public String getContentAsString() { + @Override + public String getContentAsString() { return null; } }; @@ -88,34 +126,83 @@ public class WireMockSnippetTests { private OperationRequest request() { return new OperationRequest() { - @Override public byte[] getContent() { + @Override + public byte[] getContent() { return new byte[0]; } - @Override public String getContentAsString() { + @Override + public String getContentAsString() { return null; } - @Override public HttpHeaders getHeaders() { + @Override + public HttpHeaders getHeaders() { return new HttpHeaders(); } - @Override public HttpMethod getMethod() { + @Override + public HttpMethod getMethod() { return HttpMethod.GET; } - @Override public Parameters getParameters() { + @Override + public Parameters getParameters() { return null; } - @Override public Collection getParts() { + @Override + public Collection getParts() { return null; } - @Override public URI getUri() { + @Override + public URI getUri() { return URI.create("http://foo/bar"); } }; } + private OperationRequest requestPostWithJsonContentType() { + return new OperationRequest() { + + @Override + public byte[] getContent() { + String content = "{\"name\": \"12\"}"; + return content.getBytes(Charset.forName("UTF-8")); + } + + @Override + public String getContentAsString() { + return "{\"name\": \"12\"}"; + } + + @Override + public HttpHeaders getHeaders() { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add("Content-Type", MediaType.APPLICATION_JSON_VALUE); + return httpHeaders; + } + + @Override + public HttpMethod getMethod() { + return HttpMethod.POST; + } + + @Override + public Parameters getParameters() { + return null; + } + + @Override + public Collection getParts() { + return null; + } + + @Override + public URI getUri() { + return URI.create("http://foo/bar"); + } + }; + } } \ No newline at end of file