Webtestclient support (#757)
* Fix incorrect spring cloud dependencies versioning. * Test fix and minor refactoring. * Add docs. * Fixes after code review. fixes gh-422
This commit is contained in:
committed by
Marcin Grzejszczak
parent
fea5c743ce
commit
cf21f18938
@@ -180,8 +180,7 @@ include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wirem
|
||||
|
||||
https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate
|
||||
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
|
||||
or `WebTestClient` or
|
||||
Rest Assured. At the same time that you generate documentation for your API, you can also
|
||||
or `WebTestClient` or Rest Assured. At the same time that you generate documentation for your API, you can also
|
||||
generate WireMock stubs by using Spring Cloud Contract WireMock. To do so, write your
|
||||
normal REST Docs test cases and use `@AutoConfigureRestDocs` to have stubs be
|
||||
automatically generated in the REST Docs output directory. The following code shows an
|
||||
|
||||
@@ -363,10 +363,38 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
|
||||
----
|
||||
|
||||
The preceding example uses Spring's `MockMvc` to run the tests. This is the default test
|
||||
mode for HTTP contracts. However, JAX-RX client and explicit HTTP invocations can also be
|
||||
mode for HTTP contracts. However, JAX-RS client and explicit HTTP invocations can also be
|
||||
used. (To do so, change the `testMode` property of the plugin to `JAX-RS` or `EXPLICIT`,
|
||||
respectively.)
|
||||
|
||||
Since 2.1.0, it is also possible to use `RestAssuredWebTestClient`with Spring's reactive `WebTestClient`
|
||||
run under the hood. This is particularly recommended while working with Reactive, `Web-Flux`-based applications.
|
||||
In order to use `WebTestClient` set `testMode` to `WEBTESTCLIENT`.
|
||||
|
||||
Here is an example of a test generated in `WEBTESTCLIENT` test mode:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Test
|
||||
public void validate_shouldRejectABeerIfTooYoung() throws Exception {
|
||||
// given:
|
||||
WebTestClientRequestSpecification request = given()
|
||||
.header("Content-Type", "application/json")
|
||||
.body("{\"age\":10}");
|
||||
|
||||
// when:
|
||||
WebTestClientResponse response = given().spec(request)
|
||||
.post("/check");
|
||||
|
||||
// then:
|
||||
assertThat(response.statusCode()).isEqualTo(200);
|
||||
assertThat(response.header("Content-Type")).matches("application/json.*");
|
||||
// and:
|
||||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||||
assertThatJson(parsedJson).field("['status']").isEqualTo("NOT_OK");
|
||||
}
|
||||
----
|
||||
|
||||
Apart from the default JUnit 4, you can instead use JUnit 5 or Spock tests, by setting the plugin
|
||||
`testFramework` property to either `JUNIT5` or `Spock`.
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ contracts {
|
||||
==== Configuration Options
|
||||
|
||||
* *testMode*: Defines the mode for acceptance tests. By default, the mode is MockMvc,
|
||||
which is based on Spring's MockMvc. It can also be changed to *JaxRsClient* or to
|
||||
which is based on Spring's MockMvc. It can also be changed to *WebTestClient*, *JaxRsClient* or to
|
||||
*Explicit* for real HTTP calls.
|
||||
* *imports*: Creates an array with imports that should be included in generated tests
|
||||
(for example ['org.myorg.Matchers']). By default, it creates an empty array.
|
||||
@@ -569,7 +569,7 @@ definition or the `execution` definition, as shown here:
|
||||
==== Configuration Options
|
||||
|
||||
* *testMode*: Defines the mode for acceptance tests. By default, the mode is MockMvc,
|
||||
which is based on Spring's MockMvc. It can also be changed to *JaxRsClient* or to
|
||||
which is based on Spring's MockMvc. It can also be changed to *WebTestClient*, *JaxRsClient* or to
|
||||
*Explicit* for real HTTP calls.
|
||||
* *basePackageForTests*: Specifies the base package for all generated tests. If not set,
|
||||
the value is picked from `baseClassForTests`'s package and from `packageWithBaseClasses`.
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<wiremock.version>2.19.0</wiremock.version>
|
||||
<jsonassert.version>0.4.13</jsonassert.version>
|
||||
<aether.version>1.0.2.v20150114</aether.version>
|
||||
<rest-assured.version>3.0.7</rest-assured.version>
|
||||
<rest-assured.version>3.2.0</rest-assured.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -115,6 +115,21 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-web-test-client</artifactId>
|
||||
<version>${rest-assured.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<groupId>org.springframework</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
<groupId>org.springframework</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>rest-assured</artifactId>
|
||||
|
||||
@@ -133,6 +133,16 @@
|
||||
<artifactId>spring-mock-mvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.rest-assured</groupId>
|
||||
<artifactId>spring-web-test-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
|
||||
@@ -34,9 +34,9 @@ import java.util.regex.Pattern
|
||||
*/
|
||||
@PackageScope
|
||||
@TypeChecked
|
||||
class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder {
|
||||
class HttpSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestProcessingBodyBuilder {
|
||||
|
||||
MockMvcSpockMethodRequestProcessingBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) {
|
||||
HttpSpockMethodRequestProcessingBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) {
|
||||
super(stubDefinition, configProperties)
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ class JavaTestGenerator implements SingleTestGenerator {
|
||||
clazz.addStaticImports(httpImportProvider.getStaticImports(configProperties.targetFramework, configProperties.testMode))
|
||||
}
|
||||
|
||||
// TODO for 2.2: leave only RestAssured 3
|
||||
private String getRestAssuredPackage() {
|
||||
boolean restAssured2Present = this.checker.isClassPresent(REST_ASSURED_2_0_CLASS)
|
||||
String restAssuredPackage = restAssured2Present ? 'com.jayway.restassured' : 'io.restassured'
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.contract.verifier.builder
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.util.logging.Commons
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.config.TestMode
|
||||
@@ -118,14 +117,19 @@ class MethodBuilder {
|
||||
return new JaxRsClientJUnitMethodBodyBuilder(stubContent, configProperties)
|
||||
}
|
||||
return new JaxRsClientSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
} else if (configProperties.testMode == TestMode.WEBTESTCLIENT) {
|
||||
if (isJUnitType()) {
|
||||
return new WebTestClientJUnitMethodBodyBuilder(stubContent, configProperties)
|
||||
}
|
||||
return new HttpSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
} else if (configProperties.testMode == TestMode.EXPLICIT) {
|
||||
if (isJUnitType()) {
|
||||
return new ExplicitJUnitMethodBodyBuilder(stubContent, configProperties)
|
||||
}
|
||||
// in Groovy we're using def so we don't have to update the imports
|
||||
return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
return new HttpSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
} else if (configProperties.targetFramework == SPOCK) {
|
||||
return new MockMvcSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
return new HttpSpockMethodRequestProcessingBodyBuilder(stubContent, configProperties)
|
||||
}
|
||||
return new MockMvcJUnitMethodBodyBuilder(stubContent, configProperties)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.builder
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TypeChecked
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@TypeChecked
|
||||
@CompileStatic
|
||||
@PackageScope
|
||||
class MockMvcJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder {
|
||||
|
||||
@@ -38,12 +38,12 @@ class MockMvcJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder {
|
||||
|
||||
@Override
|
||||
protected String returnedResponseType() {
|
||||
return "ResponseOptions"
|
||||
return 'ResponseOptions'
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String returnedRequestType() {
|
||||
return "MockMvcRequestSpecification"
|
||||
return 'MockMvcRequestSpecification'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.springframework.cloud.contract.verifier.builder
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.spec.internal.Url
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
/**
|
||||
* A {@link JUnitMethodBodyBuilder} implementation that uses WebTestClient to send requests.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@CompileStatic
|
||||
@PackageScope
|
||||
class WebTestClientJUnitMethodBodyBuilder extends RestAssuredJUnitMethodBodyBuilder {
|
||||
|
||||
WebTestClientJUnitMethodBodyBuilder(Contract stubDefinition, ContractVerifierConfigProperties configProperties) {
|
||||
super(stubDefinition, configProperties)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String returnedResponseType() {
|
||||
return 'WebTestClientResponse'
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String returnedRequestType() {
|
||||
return 'WebTestClientRequestSpecification'
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void when(BlockBuilder bb) {
|
||||
bb.addLine(getInputString(request))
|
||||
bb.indent()
|
||||
|
||||
Url url = getUrl(request)
|
||||
addQueryParameters(url, bb)
|
||||
addUrl(url, bb)
|
||||
addColonIfRequired(bb)
|
||||
bb.unindent()
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import static org.springframework.cloud.contract.verifier.config.TestFramework.S
|
||||
import static org.springframework.cloud.contract.verifier.config.TestMode.EXPLICIT
|
||||
import static org.springframework.cloud.contract.verifier.config.TestMode.JAXRSCLIENT
|
||||
import static org.springframework.cloud.contract.verifier.config.TestMode.MOCKMVC
|
||||
import static org.springframework.cloud.contract.verifier.config.TestMode.WEBTESTCLIENT
|
||||
|
||||
/**
|
||||
* Provides imports based on test framework and test mode.
|
||||
@@ -21,29 +22,38 @@ import static org.springframework.cloud.contract.verifier.config.TestMode.MOCKMV
|
||||
class HttpImportProvider {
|
||||
|
||||
private final Map<TestMode, ImportDefinitions> TEST_MODE_SPECIFIC_IMPORTS = [
|
||||
(JAXRSCLIENT): new ImportDefinitions([], ['javax.ws.rs.client.Entity.*']),
|
||||
(MOCKMVC) : new ImportDefinitions([], ["${restAssuredPackage}.module.mockmvc.RestAssuredMockMvc.*"]),
|
||||
(EXPLICIT) : new ImportDefinitions([], ["${restAssuredPackage}.RestAssured.*"])]
|
||||
(JAXRSCLIENT) : new ImportDefinitions([], ['javax.ws.rs.client.Entity.*']),
|
||||
(MOCKMVC) : new ImportDefinitions([], ["${restAssuredPackage}.module.mockmvc.RestAssuredMockMvc.*"]),
|
||||
(EXPLICIT) : new ImportDefinitions([], ["${restAssuredPackage}.RestAssured.*"]),
|
||||
(WEBTESTCLIENT): new ImportDefinitions([], ['io.restassured.module.webtestclient.RestAssuredWebTestClient.*'])]
|
||||
|
||||
private final Map<Tuple2<TestFramework, TestMode>, ImportDefinitions> FRAMEWORK_AND_TEST_MODE_SPECIFIC_IMPORTS = [
|
||||
(new Tuple2(JUNIT, JAXRSCLIENT)) : new ImportDefinitions(['javax.ws.rs.core.Response']),
|
||||
(new Tuple2(JUNIT5, JAXRSCLIENT)): new ImportDefinitions(['javax.ws.rs.core.Response']),
|
||||
(new Tuple2(JUNIT, MOCKMVC)) : new ImportDefinitions([
|
||||
(new Tuple2(JUNIT, JAXRSCLIENT)) : new ImportDefinitions(['javax.ws.rs.core.Response']),
|
||||
(new Tuple2(JUNIT5, JAXRSCLIENT)) : new ImportDefinitions(['javax.ws.rs.core.Response']),
|
||||
(new Tuple2(JUNIT, MOCKMVC)) : new ImportDefinitions([
|
||||
"${restAssuredPackage}.module.mockmvc.specification.MockMvcRequestSpecification",
|
||||
"${restAssuredPackage}.response.ResponseOptions"]),
|
||||
(new Tuple2(JUNIT5, MOCKMVC)) : new ImportDefinitions([
|
||||
(new Tuple2(JUNIT, WEBTESTCLIENT)) : new ImportDefinitions([
|
||||
'io.restassured.module.webtestclient.specification.WebTestClientRequestSpecification',
|
||||
'io.restassured.module.webtestclient.response.WebTestClientResponse']),
|
||||
(new Tuple2(JUNIT5, MOCKMVC)) : new ImportDefinitions([
|
||||
"${restAssuredPackage}.module.mockmvc.specification.MockMvcRequestSpecification",
|
||||
"${restAssuredPackage}.response.ResponseOptions"]),
|
||||
(new Tuple2(JUNIT, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification",
|
||||
(new Tuple2(JUNIT5, WEBTESTCLIENT)): new ImportDefinitions([
|
||||
'io.restassured.module.webtestclient.specification.WebTestClientRequestSpecification',
|
||||
'io.restassured.module.webtestclient.response.WebTestClientResponse']),
|
||||
(new Tuple2(JUNIT, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification",
|
||||
"${restAssuredPackage}.response.Response"]),
|
||||
(new Tuple2(JUNIT5, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification",
|
||||
"${restAssuredPackage}.response.Response"]),
|
||||
(new Tuple2(SPOCK, JAXRSCLIENT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, JAXRSCLIENT)): new ImportDefinitions([]),
|
||||
(new Tuple2(SPOCK, MOCKMVC)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, MOCKMVC)) : new ImportDefinitions([]),
|
||||
(new Tuple2(SPOCK, EXPLICIT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, EXPLICIT)) : new ImportDefinitions([])
|
||||
(new Tuple2(JUNIT5, EXPLICIT)) : new ImportDefinitions(["${restAssuredPackage}.specification.RequestSpecification",
|
||||
"${restAssuredPackage}.response.Response"]),
|
||||
(new Tuple2(SPOCK, JAXRSCLIENT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, JAXRSCLIENT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(SPOCK, MOCKMVC)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, MOCKMVC)) : new ImportDefinitions([]),
|
||||
(new Tuple2(SPOCK, EXPLICIT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, EXPLICIT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(SPOCK, WEBTESTCLIENT)) : new ImportDefinitions([]),
|
||||
(new Tuple2(CUSTOM, WEBTESTCLIENT)): new ImportDefinitions([])
|
||||
]
|
||||
|
||||
private final String restAssuredPackage
|
||||
|
||||
@@ -37,5 +37,10 @@ enum TestMode {
|
||||
/**
|
||||
* Uses JAX-RS client
|
||||
*/
|
||||
JAXRSCLIENT
|
||||
JAXRSCLIENT,
|
||||
|
||||
/**
|
||||
* Uses Spring's reactive WebTestClient
|
||||
*/
|
||||
WEBTESTCLIENT
|
||||
}
|
||||
@@ -282,7 +282,7 @@ class ContractHttpDocsSpec extends Specification {
|
||||
def 'should convert dsl with optionals to proper Spock test'() {
|
||||
given:
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
new MockMvcSpockMethodRequestProcessingBodyBuilder(optionals, properties).appendTo(blockBuilder)
|
||||
new HttpSpockMethodRequestProcessingBodyBuilder(optionals, properties).appendTo(blockBuilder)
|
||||
expect:
|
||||
String expectedTest =
|
||||
// tag::optionals_test[]
|
||||
|
||||
@@ -845,7 +845,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
|
||||
and:
|
||||
stubMappingIsValidWireMockStub(contractDsl)
|
||||
and:
|
||||
SyntaxChecker.tryToCompileJava(blockBuilder.toString())
|
||||
SyntaxChecker.tryToCompileJava(JaxRsClientJUnitMethodBodyBuilder.simpleName, blockBuilder.toString())
|
||||
}
|
||||
|
||||
@Issue('#85')
|
||||
@@ -974,7 +974,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
|
||||
then:
|
||||
test.contains("assertThat(responseBody).matches(\".*\");")
|
||||
and:
|
||||
SyntaxChecker.tryToCompileJava(blockBuilder.toString())
|
||||
SyntaxChecker.tryToCompileJava(JaxRsClientJUnitMethodBodyBuilder.simpleName, blockBuilder.toString())
|
||||
}
|
||||
|
||||
@Issue('#150')
|
||||
@@ -999,7 +999,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
|
||||
then:
|
||||
test.contains("responseBody ==~ java.util.regex.Pattern.compile('.*')")
|
||||
and:
|
||||
SyntaxChecker.tryToCompileGroovy(blockBuilder.toString())
|
||||
SyntaxChecker.tryToCompileGroovy(JaxRsClientJUnitMethodBodyBuilder.simpleName, blockBuilder.toString())
|
||||
}
|
||||
|
||||
@Issue('#150')
|
||||
@@ -1048,7 +1048,7 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
|
||||
test.contains("foo(responseBody)")
|
||||
and:
|
||||
// no static compilation due to bug in Groovy https://issues.apache.org/jira/browse/GROOVY-8055
|
||||
SyntaxChecker.tryToCompileGroovy(blockBuilder.toString(), false)
|
||||
SyntaxChecker.tryToCompileGroovy(JaxRsClientJUnitMethodBodyBuilder.simpleName, blockBuilder.toString(), false)
|
||||
}
|
||||
|
||||
def "should allow c/p version of consumer producer"() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -28,430 +28,438 @@ import spock.lang.Specification
|
||||
|
||||
class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements WireMockStubVerifier {
|
||||
|
||||
@Rule OutputCapture outputCapture = new OutputCapture()
|
||||
@Rule
|
||||
OutputCapture outputCapture = new OutputCapture()
|
||||
|
||||
@Shared ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(
|
||||
assertJsonSize: true
|
||||
)
|
||||
@Shared
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties(
|
||||
assertJsonSize: true
|
||||
)
|
||||
|
||||
@Issue('#185')
|
||||
def "should allow to set dynamic values via stub / test matchers for [#methodBuilderName]"() {
|
||||
given:
|
||||
//tag::matchers[]
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
urlPath '/get'
|
||||
body([
|
||||
duck: 123,
|
||||
alpha: "abc",
|
||||
number: 123,
|
||||
aBoolean: true,
|
||||
date: "2017-01-01",
|
||||
dateTime: "2017-01-01T01:23:45",
|
||||
time: "01:02:34",
|
||||
valueWithoutAMatcher: "foo",
|
||||
valueWithTypeMatch: "string",
|
||||
key: [
|
||||
'complex.key' : 'foo'
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
jsonPath('$.duck', byEquality())
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
jsonPath("\$.['key'].['complex.key']", byEquality())
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
duck: 123,
|
||||
alpha: "abc",
|
||||
number: 123,
|
||||
positiveInteger: 1234567890,
|
||||
negativeInteger: -1234567890,
|
||||
positiveDecimalNumber: 123.4567890,
|
||||
negativeDecimalNumber: -123.4567890,
|
||||
aBoolean: true,
|
||||
date: "2017-01-01",
|
||||
dateTime: "2017-01-01T01:23:45",
|
||||
time: "01:02:34",
|
||||
valueWithoutAMatcher: "foo",
|
||||
valueWithTypeMatch: "string",
|
||||
valueWithMin: [
|
||||
1,2,3
|
||||
],
|
||||
valueWithMax: [
|
||||
1,2,3
|
||||
],
|
||||
valueWithMinMax: [
|
||||
1,2,3
|
||||
],
|
||||
valueWithMinEmpty: [],
|
||||
valueWithMaxEmpty: [],
|
||||
key: [
|
||||
'complex.key' : 'foo'
|
||||
],
|
||||
nullValue: null
|
||||
])
|
||||
bodyMatchers {
|
||||
// asserts the jsonpath value against manual regex
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
// asserts the jsonpath value against the provided value
|
||||
jsonPath('$.duck', byEquality())
|
||||
// asserts the jsonpath value against some default regex
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.positiveInteger', byRegex(anInteger()))
|
||||
jsonPath('$.negativeInteger', byRegex(anInteger()))
|
||||
jsonPath('$.positiveDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.negativeDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
// asserts vs inbuilt time related regex
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
// asserts that the resulting type is the same as in response body
|
||||
jsonPath('$.valueWithTypeMatch', byType())
|
||||
jsonPath('$.valueWithMin', byType {
|
||||
// results in verification of size of array (min 1)
|
||||
minOccurrence(1)
|
||||
})
|
||||
jsonPath('$.valueWithMax', byType {
|
||||
// results in verification of size of array (max 3)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
jsonPath('$.valueWithMinMax', byType {
|
||||
// results in verification of size of array (min 1 & max 3)
|
||||
minOccurrence(1)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
jsonPath('$.valueWithMinEmpty', byType {
|
||||
// results in verification of size of array (min 0)
|
||||
minOccurrence(0)
|
||||
})
|
||||
jsonPath('$.valueWithMaxEmpty', byType {
|
||||
// results in verification of size of array (max 0)
|
||||
maxOccurrence(0)
|
||||
})
|
||||
// will execute a method `assertThatValueIsANumber`
|
||||
jsonPath('$.duck', byCommand('assertThatValueIsANumber($it)'))
|
||||
jsonPath("\$.['key'].['complex.key']", byEquality())
|
||||
jsonPath('$.nullValue', byNull())
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
header('Some-Header', $(c('someValue'), p(regex('[a-zA-Z]{9}'))))
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::matchers[]
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.duck", String.class)).matches("[0-9]{3}")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.duck", Integer.class)).isEqualTo(123)')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.alpha", String.class)).matches("[\\\\p{L}]*")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.alpha", String.class)).isEqualTo("abc")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.number", String.class)).matches("-?(\\\\d*\\\\.\\\\d+|\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.positiveInteger", String.class)).matches("-?(\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.negativeInteger", String.class)).matches("-?(\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.positiveDecimalNumber", String.class)).matches("-?(\\\\d*\\\\.\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.negativeDecimalNumber", String.class)).matches("-?(\\\\d*\\\\.\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.aBoolean", String.class)).matches("(true|false)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.date", String.class)).matches("(\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.dateTime", String.class)).matches("([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithTypeMatch")).isInstanceOf(java.lang.String.class)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMin")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMin", java.util.Collection.class)).as("' + rootElement + '.valueWithMin").hasSizeGreaterThanOrEqualTo(1)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMax")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMax", java.util.Collection.class)).as("' + rootElement + '.valueWithMax").hasSizeLessThanOrEqualTo(3)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMinMax")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMinMax", java.util.Collection.class)).as("' + rootElement + '.valueWithMinMax").hasSizeBetween(1, 3)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMinEmpty")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMinEmpty", java.util.Collection.class)).as("' + rootElement + '.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMaxEmpty")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMaxEmpty", java.util.Collection.class)).as("' + rootElement + '.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0)')
|
||||
test.contains('assertThatValueIsANumber(parsedJson.read("' + rootElement + '.duck")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '''.['key'].['complex.key']", String.class)).isEqualTo("foo")''')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.nullValue")).isNull()')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch (ClassFormatError classFormatError) {
|
||||
String output = outputCapture.toString()
|
||||
assert output.contains('error: cannot find symbol')
|
||||
assert output.contains('assertThatValueIsANumber(parsedJson.read("$.duck"));')
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#185')
|
||||
def 'should allow to set dynamic values via stub / test matchers for [#methodBuilderName]'() {
|
||||
given:
|
||||
//tag::matchers[]
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
urlPath '/get'
|
||||
body([
|
||||
duck : 123,
|
||||
alpha : 'abc',
|
||||
number : 123,
|
||||
aBoolean : true,
|
||||
date : '2017-01-01',
|
||||
dateTime : '2017-01-01T01:23:45',
|
||||
time : '01:02:34',
|
||||
valueWithoutAMatcher: 'foo',
|
||||
valueWithTypeMatch : 'string',
|
||||
key : [
|
||||
'complex.key': 'foo'
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
jsonPath('$.duck', byEquality())
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
jsonPath("\$.['key'].['complex.key']", byEquality())
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
duck : 123,
|
||||
alpha : 'abc',
|
||||
number : 123,
|
||||
positiveInteger : 1234567890,
|
||||
negativeInteger : -1234567890,
|
||||
positiveDecimalNumber: 123.4567890,
|
||||
negativeDecimalNumber: -123.4567890,
|
||||
aBoolean : true,
|
||||
date : '2017-01-01',
|
||||
dateTime : '2017-01-01T01:23:45',
|
||||
time : "01:02:34",
|
||||
valueWithoutAMatcher : 'foo',
|
||||
valueWithTypeMatch : 'string',
|
||||
valueWithMin : [
|
||||
1, 2, 3
|
||||
],
|
||||
valueWithMax : [
|
||||
1, 2, 3
|
||||
],
|
||||
valueWithMinMax : [
|
||||
1, 2, 3
|
||||
],
|
||||
valueWithMinEmpty : [],
|
||||
valueWithMaxEmpty : [],
|
||||
key : [
|
||||
'complex.key': 'foo'
|
||||
],
|
||||
nullValue : null
|
||||
])
|
||||
bodyMatchers {
|
||||
// asserts the jsonpath value against manual regex
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
// asserts the jsonpath value against the provided value
|
||||
jsonPath('$.duck', byEquality())
|
||||
// asserts the jsonpath value against some default regex
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.positiveInteger', byRegex(anInteger()))
|
||||
jsonPath('$.negativeInteger', byRegex(anInteger()))
|
||||
jsonPath('$.positiveDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.negativeDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
// asserts vs inbuilt time related regex
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
// asserts that the resulting type is the same as in response body
|
||||
jsonPath('$.valueWithTypeMatch', byType())
|
||||
jsonPath('$.valueWithMin', byType {
|
||||
// results in verification of size of array (min 1)
|
||||
minOccurrence(1)
|
||||
})
|
||||
jsonPath('$.valueWithMax', byType {
|
||||
// results in verification of size of array (max 3)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
jsonPath('$.valueWithMinMax', byType {
|
||||
// results in verification of size of array (min 1 & max 3)
|
||||
minOccurrence(1)
|
||||
maxOccurrence(3)
|
||||
})
|
||||
jsonPath('$.valueWithMinEmpty', byType {
|
||||
// results in verification of size of array (min 0)
|
||||
minOccurrence(0)
|
||||
})
|
||||
jsonPath('$.valueWithMaxEmpty', byType {
|
||||
// results in verification of size of array (max 0)
|
||||
maxOccurrence(0)
|
||||
})
|
||||
// will execute a method `assertThatValueIsANumber`
|
||||
jsonPath('$.duck', byCommand('assertThatValueIsANumber($it)'))
|
||||
jsonPath("\$.['key'].['complex.key']", byEquality())
|
||||
jsonPath('$.nullValue', byNull())
|
||||
}
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
header('Some-Header', $(c('someValue'), p(regex('[a-zA-Z]{9}'))))
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::matchers[]
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.duck", String.class)).matches("[0-9]{3}")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.duck", Integer.class)).isEqualTo(123)')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.alpha", String.class)).matches("[\\\\p{L}]*")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.alpha", String.class)).isEqualTo("abc")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.number", String.class)).matches("-?(\\\\d*\\\\.\\\\d+|\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.positiveInteger", String.class)).matches("-?(\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.negativeInteger", String.class)).matches("-?(\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.positiveDecimalNumber", String.class)).matches("-?(\\\\d*\\\\.\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.negativeDecimalNumber", String.class)).matches("-?(\\\\d*\\\\.\\\\d+)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.aBoolean", String.class)).matches("(true|false)")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.date", String.class)).matches("(\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.dateTime", String.class)).matches("([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])")')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithTypeMatch")).isInstanceOf(java.lang.String.class)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMin")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMin", java.util.Collection.class)).as("' + rootElement + '.valueWithMin").hasSizeGreaterThanOrEqualTo(1)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMax")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMax", java.util.Collection.class)).as("' + rootElement + '.valueWithMax").hasSizeLessThanOrEqualTo(3)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMinMax")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMinMax", java.util.Collection.class)).as("' + rootElement + '.valueWithMinMax").hasSizeBetween(1, 3)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMinEmpty")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMinEmpty", java.util.Collection.class)).as("' + rootElement + '.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0)')
|
||||
test.contains('assertThat((Object) parsedJson.read("' + rootElement + '.valueWithMaxEmpty")).isInstanceOf(java.util.List.class)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.valueWithMaxEmpty", java.util.Collection.class)).as("' + rootElement + '.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0)')
|
||||
test.contains('assertThatValueIsANumber(parsedJson.read("' + rootElement + '.duck")')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '''.['key'].['complex.key']", String.class)).isEqualTo("foo")''')
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '.nullValue")).isNull()')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch (ClassFormatError classFormatError) {
|
||||
String output = outputCapture.toString()
|
||||
assert output.contains('error: cannot find symbol')
|
||||
assert output.contains('assertThatValueIsANumber(parsedJson.read("$.duck"));')
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
@Issue('#217')
|
||||
def "should allow complex matchers for [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"firstName": "Jane",
|
||||
"lastName": "Doe",
|
||||
"isAlive": true,
|
||||
"address": [
|
||||
"postalCode": "98101",
|
||||
],
|
||||
"phoneNumbers": [
|
||||
[
|
||||
"type": "home",
|
||||
"number": "999 999-9999",
|
||||
]
|
||||
],
|
||||
"gender": [
|
||||
"type": "female",
|
||||
],
|
||||
"children": [
|
||||
[
|
||||
"firstName": "Kid",
|
||||
"age": 55,
|
||||
]
|
||||
],
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers', byType {
|
||||
minOccurrence(0) // min occurrence of 1
|
||||
maxOccurrence(4) // max occurrence of 3
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byRegex("^[0-9]{3} [0-9]{3}-[0-9]{4}\$"))
|
||||
jsonPath('$..number', byRegex("^[0-9]{3} [0-9]{3}-[0-9]{4}\$"))
|
||||
}
|
||||
headers {
|
||||
contentType('application/json')
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").allElementsMatch("^[0-9]{3} [0-9]{3}-[0-9]{4}' + rootElement + '")')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '..number", java.util.Collection.class)).as("' + rootElement + '..number").allElementsMatch("^[0-9]{3} [0-9]{3}-[0-9]{4}' + rootElement + '")')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch(NoClassDefFoundError error) {
|
||||
// that's actually expected since we're creating an anonymous class
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#217')
|
||||
def 'should allow complex matchers for [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"firstName" : 'Jane',
|
||||
"lastName" : 'Doe',
|
||||
"isAlive" : true,
|
||||
"address" : [
|
||||
"postalCode": '98101',
|
||||
],
|
||||
"phoneNumbers": [
|
||||
[
|
||||
"type" : 'home',
|
||||
"number": '999 999-9999',
|
||||
]
|
||||
],
|
||||
"gender" : [
|
||||
"type": 'female',
|
||||
],
|
||||
"children" : [
|
||||
[
|
||||
"firstName": 'Kid',
|
||||
"age" : 55,
|
||||
]
|
||||
],
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers', byType {
|
||||
minOccurrence(0) // min occurrence of 1
|
||||
maxOccurrence(4) // max occurrence of 3
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byRegex("^[0-9]{3} [0-9]{3}-[0-9]{4}\$"))
|
||||
jsonPath('$..number', byRegex("^[0-9]{3} [0-9]{3}-[0-9]{4}\$"))
|
||||
}
|
||||
headers {
|
||||
contentType('application/json')
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").allElementsMatch("^[0-9]{3} [0-9]{3}-[0-9]{4}' + rootElement + '")')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '..number", java.util.Collection.class)).as("' + rootElement + '..number").allElementsMatch("^[0-9]{3} [0-9]{3}-[0-9]{4}' + rootElement + '")')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch (NoClassDefFoundError error) {
|
||||
// that's actually expected since we're creating an anonymous class
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
|
||||
@Issue('#217')
|
||||
def "should use the flattened assertions when jsonpath contains [*] for [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: "foo"
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
minOccurrence(0)
|
||||
maxOccurrence(4)
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
minOccurrence(0)
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
maxOccurrence(4)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeBetween(0, 4)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeGreaterThanOrEqualTo(0)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeLessThanOrEqualTo(4)')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch(NoClassDefFoundError error) {
|
||||
// that's actually expected since we're creating an anonymous class
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#217')
|
||||
def 'should use the flattened assertions when jsonpath contains [*] for [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: 'foo'
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
minOccurrence(0)
|
||||
maxOccurrence(4)
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
minOccurrence(0)
|
||||
})
|
||||
jsonPath('$.phoneNumbers[*].number', byType {
|
||||
maxOccurrence(4)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeBetween(0, 4)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeGreaterThanOrEqualTo(0)')
|
||||
test.contains('assertThat((java.lang.Iterable) parsedJson.read("' + rootElement + '.phoneNumbers[*].number", java.util.Collection.class)).as("' + rootElement + '.phoneNumbers[*].number").hasFlattenedSizeLessThanOrEqualTo(4)')
|
||||
!test.contains('cursor')
|
||||
and:
|
||||
try {
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, blockBuilder.toString())
|
||||
} catch (NoClassDefFoundError error) {
|
||||
// that's actually expected since we're creating an anonymous class
|
||||
}
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
@Issue('#217')
|
||||
def "should allow matcher with command to execute [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: "foo"
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers[*].number', byCommand('foo($it)'))
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('foo(parsedJson.read("' + rootElement + '.phoneNumbers[*].number")')
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#217')
|
||||
def 'should allow matcher with command to execute [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: 'foo'
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.phoneNumbers[*].number', byCommand('foo($it)'))
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('foo(parsedJson.read("' + rootElement + '.phoneNumbers[*].number")')
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
@Issue('#217')
|
||||
def "should throw an exception when command to execute references a non existing entry in the body [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: "foo"
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.nonExistingPhoneNumbers[*].number', byCommand('foo($it)'))
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
IllegalStateException e = thrown(IllegalStateException)
|
||||
e.message.contains("Entry for the provided JSON path <\$.nonExistingPhoneNumbers[*].number> doesn't exist in the body")
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#217')
|
||||
def 'should throw an exception when command to execute references a non existing entry in the body [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url 'person'
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"phoneNumbers": [
|
||||
number: 'foo'
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.nonExistingPhoneNumbers[*].number', byCommand('foo($it)'))
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
when:
|
||||
builder.appendTo(blockBuilder)
|
||||
then:
|
||||
IllegalStateException e = thrown(IllegalStateException)
|
||||
e.message.contains('Entry for the provided JSON path <$.nonExistingPhoneNumbers[*].number> doesn\'t exist in the body')
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
@Issue("#229")
|
||||
def "should work for matchers and body with json array[#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url '/api/v1/xxxx'
|
||||
body(12000)
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body ([[
|
||||
[ access_token: '123']
|
||||
]])
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
bodyMatchers {
|
||||
jsonPath('''$[0][0].access_token''', byEquality())
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '[0][0].access_token", String.class)).isEqualTo("123")')
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
@Issue('#229')
|
||||
def 'should work for matchers and body with json array[#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
method 'GET'
|
||||
url '/api/v1/xxxx'
|
||||
body(12000)
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([[
|
||||
[access_token: '123']
|
||||
]])
|
||||
headers {
|
||||
contentType(applicationJson())
|
||||
}
|
||||
bodyMatchers {
|
||||
jsonPath('''$[0][0].access_token''', byEquality())
|
||||
}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
test.contains('assertThat(parsedJson.read("' + rootElement + '[0][0].access_token", String.class)).isEqualTo("123")')
|
||||
where:
|
||||
methodBuilderName | methodBuilder | rootElement
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) } | '$'
|
||||
}
|
||||
|
||||
@Issue("#391")
|
||||
def "should work for matchers and body with multiline string for [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
name "ISSUE 391"
|
||||
method 'GET'
|
||||
urlPath '/item/factsheet?size=2&page=1'
|
||||
headers { header "accept", "application/...json" }
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body("""
|
||||
@Issue('#391')
|
||||
def 'should work for matchers and body with multiline string for [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
name 'ISSUE 391'
|
||||
method 'GET'
|
||||
urlPath '/item/factsheet?size=2&page=1'
|
||||
headers { header 'accept', 'application/...json' }
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body('''
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
@@ -460,73 +468,75 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
|
||||
}
|
||||
]
|
||||
}
|
||||
""")
|
||||
bodyMatchers {
|
||||
jsonPath('$.items[*].id', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*].title', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*]', byType { minOccurrence(2); maxOccurrence(2) })
|
||||
}
|
||||
headers {header "content-type", "application/...json;charset=UTF-8"}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
!test.contains('''assertThatJson(parsedJson).array("['items']").isEmpty()''')
|
||||
where:
|
||||
methodBuilderName | methodBuilder
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) }
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
}
|
||||
''')
|
||||
bodyMatchers {
|
||||
jsonPath('$.items[*].id', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*].title', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*]', byType { minOccurrence(2); maxOccurrence(2) })
|
||||
}
|
||||
headers { header 'content-type', 'application/...json;charset=UTF-8' }
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
!test.contains('''assertThatJson(parsedJson).array("['items']").isEmpty()''')
|
||||
where:
|
||||
methodBuilderName | methodBuilder
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) }
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
}
|
||||
|
||||
@Issue("#391")
|
||||
def "should work for matchers and body with multiline string with map body for [#methodBuilderName]"() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
name "ISSUE 391"
|
||||
method 'GET'
|
||||
urlPath '/item/factsheet?size=2&page=1'
|
||||
headers { header "accept", "application/...json" }
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"items": [
|
||||
"id" : "35309",
|
||||
"title": "lorem ipsum"
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.items[*].id', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*].title', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*]', byType { minOccurrence(2); maxOccurrence(2) })
|
||||
}
|
||||
headers {header "content-type", "application/...json;charset=UTF-8"}
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
!test.contains('''assertThatJson(parsedJson).array("['items']").isEmpty()''')
|
||||
where:
|
||||
methodBuilderName | methodBuilder
|
||||
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
"MockMvcJUnitMethodBuilder" | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) }
|
||||
"JaxRsClientSpockMethodRequestProcessingBodyBuilder" | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
"JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
}
|
||||
@Issue('#391')
|
||||
def 'should work for matchers and body with multiline string with map body for [#methodBuilderName]'() {
|
||||
given:
|
||||
Contract contractDsl = Contract.make {
|
||||
request {
|
||||
name "ISSUE 391"
|
||||
method 'GET'
|
||||
urlPath '/item/factsheet?size=2&page=1'
|
||||
headers { header 'accept', 'application/...json' }
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
"items": [
|
||||
"id" : "35309",
|
||||
"title": "lorem ipsum"
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.items[*].id', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*].title', byRegex(nonBlank()))
|
||||
jsonPath('$.items[*]', byType { minOccurrence(2); maxOccurrence(2) })
|
||||
}
|
||||
headers { header 'content-type', 'application/...json;charset=UTF-8' }
|
||||
}
|
||||
}
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
BlockBuilder blockBuilder = new BlockBuilder(" ")
|
||||
and:
|
||||
builder.appendTo(blockBuilder)
|
||||
String test = blockBuilder.toString()
|
||||
when:
|
||||
SyntaxChecker.tryToCompile(methodBuilderName, test)
|
||||
then:
|
||||
!test.contains('''assertThatJson(parsedJson).array("['items']").isEmpty()''')
|
||||
where:
|
||||
methodBuilderName | methodBuilder
|
||||
HttpSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new HttpSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
MockMvcJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties) }
|
||||
JaxRsClientSpockMethodRequestProcessingBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) }
|
||||
JaxRsClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
WebTestClientJUnitMethodBodyBuilder.simpleName | { Contract dsl -> new WebTestClientJUnitMethodBodyBuilder(dsl, properties) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,14 @@
|
||||
package org.springframework.cloud.contract.verifier.util
|
||||
|
||||
import java.lang.reflect.Method
|
||||
import javax.inject.Inject
|
||||
import javax.ws.rs.client.Entity
|
||||
import javax.ws.rs.client.WebTarget
|
||||
import javax.ws.rs.core.Response
|
||||
|
||||
import com.jayway.jsonpath.DocumentContext
|
||||
import com.jayway.jsonpath.JsonPath
|
||||
import com.toomuchcoding.jsonassert.JsonAssertion
|
||||
import groovy.transform.CompileStatic
|
||||
import io.restassured.RestAssured
|
||||
import io.restassured.module.mockmvc.RestAssuredMockMvc
|
||||
import io.restassured.module.webtestclient.RestAssuredWebTestClient
|
||||
import io.restassured.module.webtestclient.response.WebTestClientResponse
|
||||
import io.restassured.module.webtestclient.specification.WebTestClientRequestSpecification
|
||||
import io.restassured.response.ResponseOptions
|
||||
import org.codehaus.groovy.control.CompilerConfiguration
|
||||
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
|
||||
@@ -19,7 +16,6 @@ import org.codehaus.groovy.control.customizers.ImportCustomizer
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mdkt.compiler.InMemoryJavaCompiler
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.assertion.SpringCloudContractAssertions
|
||||
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage
|
||||
@@ -28,6 +24,12 @@ import org.springframework.cloud.contract.verifier.messaging.internal.ContractVe
|
||||
import org.springframework.cloud.contract.verifier.messaging.util.ContractVerifierMessagingUtil
|
||||
import org.springframework.util.ReflectionUtils
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.ws.rs.client.Entity
|
||||
import javax.ws.rs.client.WebTarget
|
||||
import javax.ws.rs.core.Response
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* checking the syntax of produced scripts
|
||||
*/
|
||||
@@ -40,8 +42,8 @@ class SyntaxChecker {
|
||||
private static final String[] DEFAULT_IMPORTS = [
|
||||
Contract.name,
|
||||
ResponseOptions.name,
|
||||
"io.restassured.module.mockmvc.specification.*",
|
||||
"io.restassured.module.mockmvc.*",
|
||||
'io.restassured.module.mockmvc.specification.*',
|
||||
'io.restassured.module.mockmvc.*',
|
||||
Test.name,
|
||||
Rule.name,
|
||||
DocumentContext.name,
|
||||
@@ -51,7 +53,9 @@ class SyntaxChecker {
|
||||
ContractVerifierMessage.name,
|
||||
ContractVerifierMessaging.name,
|
||||
WebTarget.name,
|
||||
Response.name
|
||||
Response.name,
|
||||
WebTestClientRequestSpecification.name,
|
||||
WebTestClientResponse.name
|
||||
]
|
||||
|
||||
private static final String DEFAULT_IMPORTS_AS_STRING = DEFAULT_IMPORTS.collect {
|
||||
@@ -68,20 +72,28 @@ class SyntaxChecker {
|
||||
"${SpringCloudContractAssertions.name}.assertThat"
|
||||
].collect { "import static ${it};"}.join("\n")
|
||||
|
||||
private static final String WEB_TEST_CLIENT_STATIC_IMPORTS = [
|
||||
"${RestAssuredWebTestClient.name}.*",
|
||||
"${Entity.name}.*",
|
||||
"${ContractVerifierMessagingUtil.name}.headers",
|
||||
"${JsonAssertion.name}.assertThatJson",
|
||||
"${SpringCloudContractAssertions.name}.assertThat"
|
||||
].collect { "import static ${it};" }.join("\n")
|
||||
|
||||
static void tryToCompile(String builderName, String test) {
|
||||
if (builderName.toLowerCase().contains("spock")) {
|
||||
tryToCompileGroovy(test)
|
||||
tryToCompileGroovy(builderName, test)
|
||||
} else {
|
||||
tryToCompileJava(test)
|
||||
tryToCompileJava(builderName, test)
|
||||
}
|
||||
}
|
||||
|
||||
static void tryToRun(String builderName, String test) {
|
||||
if (builderName.toLowerCase().contains("spock")) {
|
||||
Script script = tryToCompileGroovy(test)
|
||||
Script script = tryToCompileGroovy(builderName, test)
|
||||
script.run()
|
||||
} else {
|
||||
Class clazz = tryToCompileJava(test)
|
||||
Class clazz = tryToCompileJava(builderName, test)
|
||||
Method method = ReflectionUtils.findMethod(clazz, "method")
|
||||
method.invoke(clazz.newInstance())
|
||||
}
|
||||
@@ -90,13 +102,13 @@ class SyntaxChecker {
|
||||
// no static compilation due to bug in Groovy https://issues.apache.org/jira/browse/GROOVY-8055
|
||||
static void tryToCompileWithoutCompileStatic(String builderName, String test) {
|
||||
if (builderName.toLowerCase().contains("spock")) {
|
||||
tryToCompileGroovy(test, false)
|
||||
tryToCompileGroovy(builderName, test, false)
|
||||
} else {
|
||||
tryToCompileJava(test)
|
||||
tryToCompileJava(builderName, test)
|
||||
}
|
||||
}
|
||||
|
||||
static Script tryToCompileGroovy(String test, boolean compileStatic = true) {
|
||||
static Script tryToCompileGroovy(String builderName, String test, boolean compileStatic = true) {
|
||||
def imports = new ImportCustomizer()
|
||||
CompilerConfiguration configuration = new CompilerConfiguration()
|
||||
if (compileStatic) {
|
||||
@@ -106,7 +118,7 @@ class SyntaxChecker {
|
||||
configuration.addCompilationCustomizers(imports)
|
||||
StringBuilder sourceCode = new StringBuilder()
|
||||
sourceCode.append("${DEFAULT_IMPORTS_AS_STRING}\n")
|
||||
sourceCode.append("${STATIC_IMPORTS}\n")
|
||||
sourceCode.append(getStaticImports(builderName))
|
||||
sourceCode.append("\n")
|
||||
sourceCode.append("WebTarget webTarget")
|
||||
sourceCode.append("\n")
|
||||
@@ -114,7 +126,14 @@ class SyntaxChecker {
|
||||
return new GroovyShell(SyntaxChecker.classLoader, configuration).parse(sourceCode.toString())
|
||||
}
|
||||
|
||||
static Class tryToCompileJava(String test) {
|
||||
private static GString getStaticImports(String builderName) {
|
||||
if (builderName.toLowerCase().contains('webtestclient')) {
|
||||
return "$WEB_TEST_CLIENT_STATIC_IMPORTS\n"
|
||||
}
|
||||
return "$STATIC_IMPORTS\n"
|
||||
}
|
||||
|
||||
static Class tryToCompileJava(String builderName, String test) {
|
||||
Random random = new Random()
|
||||
int first = Math.abs(random.nextInt())
|
||||
int hashCode = Math.abs(test.hashCode())
|
||||
@@ -123,7 +142,7 @@ class SyntaxChecker {
|
||||
String fqnClassName = "com.example.${className}"
|
||||
sourceCode.append("package com.example;\n")
|
||||
sourceCode.append("${DEFAULT_IMPORTS_AS_STRING}\n")
|
||||
sourceCode.append("${STATIC_IMPORTS}\n")
|
||||
sourceCode.append(getStaticImports(builderName))
|
||||
sourceCode.append("\n")
|
||||
sourceCode.append("public class ${className} {\n")
|
||||
sourceCode.append("\n")
|
||||
|
||||
Reference in New Issue
Block a user