From be527dc78fefe7df88895c619bea77069492279b Mon Sep 17 00:00:00 2001
From: buildmaster
Date: Mon, 2 Apr 2018 20:30:49 +0000
Subject: [PATCH] Sync docs from master to gh-pages
---
multi/multi__contract_dsl.html | 92 ++++++---
...lti__using_the_pluggable_architecture.html | 96 +++++++--
single/spring-cloud-contract.html | 188 ++++++++++++-----
spring-cloud-contract.xml | 193 ++++++++++++++----
4 files changed, 433 insertions(+), 136 deletions(-)
diff --git a/multi/multi__contract_dsl.html b/multi/multi__contract_dsl.html
index 236a123927..8a2e5dd3e0 100644
--- a/multi/multi__contract_dsl.html
+++ b/multi/multi__contract_dsl.html
@@ -81,6 +81,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -89,6 +90,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -145,6 +149,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -153,6 +158,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -350,6 +358,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -358,6 +367,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -549,8 +561,9 @@ methods to provide a meaningful status instead of a digit. E.g. you can call
OK() for a status 200 or BAD_REQUEST() for 400.
8.5 Dynamic properties
The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
want to force the consumers to stub their clocks to always return the same value of time
so that it gets matched by the stub.
For Groovy DSL you can provide the dynamic parts in your contracts
-in two ways: pass them directly in the body or set them in separate sections called
-testMatchers and stubMatchers.
For YAML you can only use the matchers section.
8.5.1 Dynamic properties inside the body
Important
This section is valid only for Groovy DSL. Check out the
+in two ways: pass them directly in the body or set them in a separate section called
+bodyMatchers.
Note
Before 2.0.0 these were set using testMatchers and stubMatchers,
+check out the migration guide for more information.
You can set the properties inside the body either with the value method or, if you use
the Groovy map notation, with $(). The following example shows how to set dynamic
properties with the value method:
value(consumer(...), producer(...))
@@ -612,10 +625,13 @@ the provided regular expression. The following code shows an example:
In the preceding example, the opposite side of the communication has the respective data
generated for request and response.
Spring Cloud Contract comes with a series of predefined regular expressions that you can
use in your contracts, as shown in the following example:
Remember to override the applyGlobally() method and set it to false if you
want the transformation to be applied only for a mapping that explicitly requires it.
8.5.7 Dynamic Properties in the Matchers Sections
If you work with Pact, the following discussion may seem familiar.
Quite a few users are used to having a separation between the body and setting the
-dynamic parts of a contract.
You can use two separate sections:
stubMatchers, which lets you define the dynamic values that should end up in a stub.
-You can set it in the request or inputMessage part of your contract.
testMatchers, which is present in the response or outputMessage side of the
+dynamic parts of a contract.
You can use the bodyMatchers section for two reasons:
Define the dynamic values that should end up in a stub.
+You can set it in the request or inputMessage part of your contract.
Verify the result of your test.
+This section is present in the response or outputMessage side of the
contract.
Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
-following matching possibilities:
Groovy DSL
For stubMatchers:
byEquality(): The value taken from the response via the provided JSON Path must be
+following matching possibilities:
Groovy DSL
For the stubs:
byEquality(): The value taken from the response via the provided JSON Path must be
equal to the value provided in the contract.
byRegex(…): The value taken from the response via the provided JSON Path must
match the regex.
byDate(): The value taken from the response via the provided JSON Path must
match the regex for an ISO Date value.
byTimestamp(): The value taken from the response via the provided JSON Path must
match the regex for an ISO DateTime value.
byTime(): The value taken from the response via the provided JSON Path must
-match the regex for an ISO Time value.
For testMatchers:
byEquality(): The value taken from the response via the provided JSON Path must be
+match the regex for an ISO Time value.
For the verification:
byEquality(): The value taken from the response via the provided JSON Path must be
equal to the provided value in the contract.
byRegex(…): The value taken from the response via the provided JSON Path must
match the regex.
byDate(): The value taken from the response via the provided JSON Path must match
the regex for an ISO Date value.
byTimestamp(): The value taken from the response via the provided JSON Path must
@@ -1055,14 +1080,14 @@ unflattened collection, use a custom method with the byCom
passed as an input to the custom method that you provide. For example,
byCommand('foo($it)') results in calling a foo method to which the value matching the
JSON Path gets passed. The type of the object read from the JSON can be one of the
-following, depending on the JSON path:
String: If you point to a String value.
JSONArray: If you point to a List.
Map: If you point to a Map.
Number: If you point to Integer, Double, or other kind of number.
Boolean: If you point to a Boolean.
YAML. Please read the Groovy section for detailed explanation of
+following, depending on the JSON path:
String: If you point to a String value.
JSONArray: If you point to a List.
Map: If you point to a Map.
Number: If you point to Integer, Double, or other kind of number.
Boolean: If you point to a Boolean.
byNull(): The value taken from the response via the provided JSON Path must be null
YAML. Please read the Groovy section for detailed explanation of
what the types mean
For YAML the structure of a matcher looks like this
- path: $.foo
type: by_regex
value: bar
Or if you want to use one of the predefined regular expressions
[only_alpha_unicode, number, any_boolean, ip_address, hostname,
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]:
In the preceding example, you can see the dynamic portions of the contract in the
@@ -1300,7 +1343,7 @@ response:
valueWithoutAMatcher, the values of the regular expressions that the stub should
contain are explicitly set. For the valueWithoutAMatcher, the verification takes place
in the same way as without the use of matchers. In that case, the test performs an
-equality check.
For the response side in the testMatchers section, we define the dynamic parts in a
+equality check.
For the response side in the bodyMatchers section, we define the dynamic parts in a
similar manner. The only difference is that the byType matchers are also present. The
verifier engine checks four fields to verify whether the response from the test
has a value for which the JSON path matches the given field, is of the same type as the one
@@ -1311,7 +1354,7 @@ between the min and maximum occurrence.
The resulting test wou
separates the autogenerated assertions and the assertion from matchers):
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/json")
- .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\"}");
+ .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\"}}");
// when:
ResponseOptions response = given().spec(request)
@@ -1322,29 +1365,30 @@ separates the autogenerated assertions and the assertion from matchers):
Notice that, for the byCommand method, the example calls the
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).as("$.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0);
+ assertThatValueIsANumber(parsedJson.read("$.duck"));
+ assertThat(parsedJson.read("$.['key'].['complex.key']", String.class)).isEqualTo("foo");
Important
Notice that, for the byCommand method, the example calls the
assertThatValueIsANumber. This method must be defined in the test base class or be
statically imported to your tests. Notice that the byCommand call was converted to
assertThatValueIsANumber(parsedJson.read("$.duck"));. That means that the engine took
@@ -1398,7 +1442,7 @@ the method name and passed the proper JSON path as a parameter to it.
}}}
-'''
Important
If you use a matcher, then the part of the request aned response that the
+'''
Important
If you use a matcher, then the part of the request and response that the
matcher addresses with the JSON Path gets removed from the assertion. In the case of
verifying a collection, you must create matchers for all the elements of the
collection.
Consider the following example:
Contract.make {
@@ -1419,7 +1463,7 @@ collection.
Consider the following example:
'$.events[0].operation', byRegex('.+'))
jsonPath('$.events[0].eventId', byRegex('^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'))
jsonPath('$.events[0].status', byRegex('.+'))
@@ -1464,7 +1508,7 @@ content type set. Otherwise, the default of application/oc
assertThatJson(parsedJson).field("['property1']").isEqualTo("a");
'''
8.7 Async Support
If you’re using asynchronous communication on the server side (your controllers are
returning Callable, DeferredResult, and so on), then, inside your contract, you must
-provide a sync() method in the response section. The following code shows an example:
Groovy DSL.
+provide an async() method in the response section. The following code shows an example:
Groovy DSL.
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
diff --git a/multi/multi__using_the_pluggable_architecture.html b/multi/multi__using_the_pluggable_architecture.html
index fcb8146c83..0513301f81 100644
--- a/multi/multi__using_the_pluggable_architecture.html
+++ b/multi/multi__using_the_pluggable_architecture.html
@@ -48,8 +48,14 @@ conversion. Also, you must define how to perform that conversion in both directi
/META-INF/spring.factories file in which you provide the fully qualified name of your
implementation.
The following example shows a typical spring.factories file:
Spring Cloud Contract includes support for Pact representation of
-contracts. Instead of using the Groovy DSL, you can use Pact files. In this section, we
-present how to add Pact support for your project.
10.1.2 Pact Contract
Consider following example of a Pact contract, which is a file under the
+contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
+present how to add Pact support for your project. Note however that not all functionality is supported.
+Starting with v3 you can combine multiple matcher for the same element;
+you can use matchers for the body, headers, request and path; and you can use value generators.
+Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
+Next to that the request and path matchers are skipped during the conversion.
+When using a date, time or datetime value generator with a given format,
+the given format will be skipped and the ISO format will be used.
10.1.2 Pact Contract
Consider following example of a Pact contract, which is a file under the
src/test/resources/contracts folder.
{
"provider": {
"name": "Provider"
@@ -70,10 +76,36 @@ present how to add Pact support for your project.
The remainder of this section about using Pact refers to the preceding file.
10.1.3 Pact for Producers
On the producer side, you mustadd two additional dependencies to your plugin
+}
The remainder of this section about using Pact refers to the preceding file.
10.1.3 Pact for Producers
On the producer side, you must add two additional dependencies to your plugin
configuration. One is the Spring Cloud Contract Pact support, and the other represents
the current Pact version that you use.
Maven.
<plugin>
@@ -123,13 +173,13 @@ the current Pact version that you use.
On the producer side, you must add two additional dependencies to your project
dependencies. One is the Spring Cloud Contract Pact support, and the other represents the
current Pact version that you use.
Maven.
@@ -184,12 +236,12 @@ current Pact version that you use.
If you want to generate tests for languages other than Java or you are not happy with the
way the verifier builds Java tests, you can register your own implementation.
The SingleTestGenerator interface lets you register your own implementation. The
following code listing shows the SingleTestGenerator interface:
package org.springframework.cloud.contract.verifier.builder
@@ -382,7 +434,7 @@ to clone the repository and use it as a source of contracts
to generate tests or stubs.
Either via environment variables, system properties, properties set
inside the plugin or contracts repository configuration you can
tweak the downloader’s behaviour. Below you can find the list of
-properties
* git.username (plugin prop)
* stubrunner.properties.git.username (system prop)
diff --git a/single/spring-cloud-contract.html b/single/spring-cloud-contract.html
index c35fd8bde0..ac92a860e1 100644
--- a/single/spring-cloud-contract.html
+++ b/single/spring-cloud-contract.html
@@ -3351,6 +3351,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -3359,6 +3360,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -3415,6 +3419,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -3423,6 +3428,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -3620,6 +3628,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -3628,6 +3637,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -3819,8 +3831,9 @@ methods to provide a meaningful status instead of a digit. E.g. you can call
OK() for a status 200 or BAD_REQUEST() for 400.
8.5 Dynamic properties
The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
want to force the consumers to stub their clocks to always return the same value of time
so that it gets matched by the stub.
For Groovy DSL you can provide the dynamic parts in your contracts
-in two ways: pass them directly in the body or set them in separate sections called
-testMatchers and stubMatchers.
For YAML you can only use the matchers section.
8.5.1 Dynamic properties inside the body
Important
This section is valid only for Groovy DSL. Check out the
+in two ways: pass them directly in the body or set them in a separate section called
+bodyMatchers.
Note
Before 2.0.0 these were set using testMatchers and stubMatchers,
+check out the migration guide for more information.
You can set the properties inside the body either with the value method or, if you use
the Groovy map notation, with $(). The following example shows how to set dynamic
properties with the value method:
value(consumer(...), producer(...))
@@ -3882,10 +3895,13 @@ the provided regular expression. The following code shows an example:
In the preceding example, the opposite side of the communication has the respective data
generated for request and response.
Spring Cloud Contract comes with a series of predefined regular expressions that you can
use in your contracts, as shown in the following example:
Remember to override the applyGlobally() method and set it to false if you
want the transformation to be applied only for a mapping that explicitly requires it.
8.5.7 Dynamic Properties in the Matchers Sections
If you work with Pact, the following discussion may seem familiar.
Quite a few users are used to having a separation between the body and setting the
-dynamic parts of a contract.
You can use two separate sections:
stubMatchers, which lets you define the dynamic values that should end up in a stub.
-You can set it in the request or inputMessage part of your contract.
testMatchers, which is present in the response or outputMessage side of the
+dynamic parts of a contract.
You can use the bodyMatchers section for two reasons:
Define the dynamic values that should end up in a stub.
+You can set it in the request or inputMessage part of your contract.
Verify the result of your test.
+This section is present in the response or outputMessage side of the
contract.
Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
-following matching possibilities:
Groovy DSL
For stubMatchers:
byEquality(): The value taken from the response via the provided JSON Path must be
+following matching possibilities:
Groovy DSL
For the stubs:
byEquality(): The value taken from the response via the provided JSON Path must be
equal to the value provided in the contract.
byRegex(…): The value taken from the response via the provided JSON Path must
match the regex.
byDate(): The value taken from the response via the provided JSON Path must
match the regex for an ISO Date value.
byTimestamp(): The value taken from the response via the provided JSON Path must
match the regex for an ISO DateTime value.
byTime(): The value taken from the response via the provided JSON Path must
-match the regex for an ISO Time value.
For testMatchers:
byEquality(): The value taken from the response via the provided JSON Path must be
+match the regex for an ISO Time value.
For the verification:
byEquality(): The value taken from the response via the provided JSON Path must be
equal to the provided value in the contract.
byRegex(…): The value taken from the response via the provided JSON Path must
match the regex.
byDate(): The value taken from the response via the provided JSON Path must match
the regex for an ISO Date value.
byTimestamp(): The value taken from the response via the provided JSON Path must
@@ -4325,14 +4350,14 @@ unflattened collection, use a custom method with the byCom
passed as an input to the custom method that you provide. For example,
byCommand('foo($it)') results in calling a foo method to which the value matching the
JSON Path gets passed. The type of the object read from the JSON can be one of the
-following, depending on the JSON path:
String: If you point to a String value.
JSONArray: If you point to a List.
Map: If you point to a Map.
Number: If you point to Integer, Double, or other kind of number.
Boolean: If you point to a Boolean.
YAML. Please read the Groovy section for detailed explanation of
+following, depending on the JSON path:
String: If you point to a String value.
JSONArray: If you point to a List.
Map: If you point to a Map.
Number: If you point to Integer, Double, or other kind of number.
Boolean: If you point to a Boolean.
byNull(): The value taken from the response via the provided JSON Path must be null
YAML. Please read the Groovy section for detailed explanation of
what the types mean
For YAML the structure of a matcher looks like this
- path: $.foo
type: by_regex
value: bar
Or if you want to use one of the predefined regular expressions
[only_alpha_unicode, number, any_boolean, ip_address, hostname,
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]:
In the preceding example, you can see the dynamic portions of the contract in the
@@ -4570,7 +4613,7 @@ response:
valueWithoutAMatcher, the values of the regular expressions that the stub should
contain are explicitly set. For the valueWithoutAMatcher, the verification takes place
in the same way as without the use of matchers. In that case, the test performs an
-equality check.
For the response side in the testMatchers section, we define the dynamic parts in a
+equality check.
For the response side in the bodyMatchers section, we define the dynamic parts in a
similar manner. The only difference is that the byType matchers are also present. The
verifier engine checks four fields to verify whether the response from the test
has a value for which the JSON path matches the given field, is of the same type as the one
@@ -4581,7 +4624,7 @@ between the min and maximum occurrence.
The resulting test wou
separates the autogenerated assertions and the assertion from matchers):
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/json")
- .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\"}");
+ .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\"}}");
// when:
ResponseOptions response = given().spec(request)
@@ -4592,29 +4635,30 @@ separates the autogenerated assertions and the assertion from matchers):
Notice that, for the byCommand method, the example calls the
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).as("$.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0);
+ assertThatValueIsANumber(parsedJson.read("$.duck"));
+ assertThat(parsedJson.read("$.['key'].['complex.key']", String.class)).isEqualTo("foo");
Important
Notice that, for the byCommand method, the example calls the
assertThatValueIsANumber. This method must be defined in the test base class or be
statically imported to your tests. Notice that the byCommand call was converted to
assertThatValueIsANumber(parsedJson.read("$.duck"));. That means that the engine took
@@ -4668,7 +4712,7 @@ the method name and passed the proper JSON path as a parameter to it.
}}}
-'''
Important
If you use a matcher, then the part of the request aned response that the
+'''
Important
If you use a matcher, then the part of the request and response that the
matcher addresses with the JSON Path gets removed from the assertion. In the case of
verifying a collection, you must create matchers for all the elements of the
collection.
Consider the following example:
Contract.make {
@@ -4689,7 +4733,7 @@ collection.
Consider the following example:
'$.events[0].operation', byRegex('.+'))
jsonPath('$.events[0].eventId', byRegex('^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'))
jsonPath('$.events[0].status', byRegex('.+'))
@@ -4734,7 +4778,7 @@ content type set. Otherwise, the default of application/oc
assertThatJson(parsedJson).field("['property1']").isEqualTo("a");
'''
8.7 Async Support
If you’re using asynchronous communication on the server side (your controllers are
returning Callable, DeferredResult, and so on), then, inside your contract, you must
-provide a sync() method in the response section. The following code shows an example:
Groovy DSL.
+provide an async() method in the response section. The following code shows an example:
Groovy DSL.
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
@@ -5270,8 +5314,14 @@ conversion. Also, you must define how to perform that conversion in both directi
/META-INF/spring.factories file in which you provide the fully qualified name of your
implementation.
The following example shows a typical spring.factories file:
Spring Cloud Contract includes support for Pact representation of
-contracts. Instead of using the Groovy DSL, you can use Pact files. In this section, we
-present how to add Pact support for your project.
10.1.2 Pact Contract
Consider following example of a Pact contract, which is a file under the
+contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
+present how to add Pact support for your project. Note however that not all functionality is supported.
+Starting with v3 you can combine multiple matcher for the same element;
+you can use matchers for the body, headers, request and path; and you can use value generators.
+Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
+Next to that the request and path matchers are skipped during the conversion.
+When using a date, time or datetime value generator with a given format,
+the given format will be skipped and the ISO format will be used.
10.1.2 Pact Contract
Consider following example of a Pact contract, which is a file under the
src/test/resources/contracts folder.
{
"provider": {
"name": "Provider"
@@ -5292,10 +5342,36 @@ present how to add Pact support for your project.
The remainder of this section about using Pact refers to the preceding file.
10.1.3 Pact for Producers
On the producer side, you mustadd two additional dependencies to your plugin
+}
The remainder of this section about using Pact refers to the preceding file.
10.1.3 Pact for Producers
On the producer side, you must add two additional dependencies to your plugin
configuration. One is the Spring Cloud Contract Pact support, and the other represents
the current Pact version that you use.
Maven.
<plugin>
@@ -5345,13 +5439,13 @@ the current Pact version that you use.
On the producer side, you must add two additional dependencies to your project
dependencies. One is the Spring Cloud Contract Pact support, and the other represents the
current Pact version that you use.
Maven.
@@ -5406,12 +5502,12 @@ current Pact version that you use.
If you want to generate tests for languages other than Java or you are not happy with the
way the verifier builds Java tests, you can register your own implementation.
The SingleTestGenerator interface lets you register your own implementation. The
following code listing shows the SingleTestGenerator interface:
package org.springframework.cloud.contract.verifier.builder
@@ -5604,7 +5700,7 @@ to clone the repository and use it as a source of contracts
to generate tests or stubs.
Either via environment variables, system properties, properties set
inside the plugin or contracts repository configuration you can
tweak the downloader’s behaviour. Below you can find the list of
-properties
* git.username (plugin prop)
* stubrunner.properties.git.username (system prop)
diff --git a/spring-cloud-contract.xml b/spring-cloud-contract.xml
index 4e4aee084f..307c48bf01 100644
--- a/spring-cloud-contract.xml
+++ b/spring-cloud-contract.xml
@@ -5749,6 +5749,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -5757,6 +5758,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -5858,6 +5862,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -5866,6 +5871,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -6156,6 +6164,7 @@ response:
body:
foo2: bar
foo3: baz
+ nullValue: null
matchers:
body:
- path: $.foo2
@@ -6164,6 +6173,9 @@ response:
- path: $.foo3
type: by_command
value: executeMe($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
- key: foo2
regex: bar
@@ -6438,8 +6450,12 @@ methods to provide a meaningful status instead of a digit. E.g. you can call
want to force the consumers to stub their clocks to always return the same value of time
so that it gets matched by the stub.
For Groovy DSL you can provide the dynamic parts in your contracts
-in two ways: pass them directly in the body or set them in separate sections called
-testMatchers and stubMatchers.
+in two ways: pass them directly in the body or set them in a separate section called
+bodyMatchers.
+
+Before 2.0.0 these were set using testMatchers and stubMatchers,
+check out the migration guide for more information.
+For YAML you can only use the matchers section.Dynamic properties inside the body
@@ -6526,10 +6542,13 @@ generated for request and response.
Spring Cloud Contract comes with a series of predefined regular expressions that you can
use in your contracts, as shown in the following example:protected static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
+protected static final Pattern ALPHA_NUMERIC = Pattern.compile('[a-zA-Z0-9]+')
protected static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
protected static final Pattern NUMBER = Pattern.compile('-?(\\d*\\.\\d+|\\d+)')
+protected static final Pattern INTEGER = Pattern.compile('-?(\\d+)')
protected static final Pattern POSITIVE_INT = Pattern.compile('([1-9]\\d*)')
protected static final Pattern DOUBLE = Pattern.compile('-?(\\d*\\.\\d+)')
+protected static final Pattern HEX = Pattern.compile('[a-fA-F0-9]+')
protected static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])')
protected static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]{1,5})?')
protected static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}')
@@ -6551,6 +6570,10 @@ Pattern onlyAlphaUnicode() {
return ONLY_ALPHA_UNICODE
}
+Pattern alphaNumeric() {
+ return ALPHA_NUMERIC
+}
+
Pattern number() {
return NUMBER
}
@@ -6563,6 +6586,10 @@ Pattern anyBoolean() {
return TRUE_OR_FALSE
}
+Pattern anInteger() {
+ return INTEGER
+}
+
Pattern aDouble() {
return DOUBLE
}
@@ -7117,14 +7144,15 @@ want the transformation to be applied only for a mapping that explicitly require
If you work with Pact, the following discussion may seem familiar.
Quite a few users are used to having a separation between the body and setting the
dynamic parts of a contract.
-You can use two separate sections:
+You can use the bodyMatchers section for two reasons:
-stubMatchers, which lets you define the dynamic values that should end up in a stub.
+Define the dynamic values that should end up in a stub.
You can set it in the request or inputMessage part of your contract.
-testMatchers, which is present in the response or outputMessage side of the
+Verify the result of your test.
+This section is present in the response or outputMessage side of the
contract.
@@ -7133,7 +7161,7 @@ following matching possibilities:
Groovy DSL
-For stubMatchers:
+For the stubs:byEquality(): The value taken from the response via the provided JSON Path must be
@@ -7158,7 +7186,7 @@ match the regex for an ISO Time value.
-For testMatchers:
+For the verification:byEquality(): The value taken from the response via the provided JSON Path must be
@@ -7211,6 +7239,9 @@ following, depending on the JSON path:
+
+byNull(): The value taken from the response via the provided JSON Path must be null
+
@@ -7280,6 +7311,9 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
by_command
+
+by_null
+
@@ -7305,7 +7339,7 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
'complex.key' : 'foo'
]
])
- stubMatchers {
+ bodyMatchers {
jsonPath('$.duck', byRegex("[0-9]{3}"))
jsonPath('$.duck', byEquality())
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
@@ -7327,6 +7361,10 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
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",
@@ -7346,9 +7384,10 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
valueWithMaxEmpty: [],
key: [
'complex.key' : 'foo'
- ]
+ ],
+ nullValue: null
])
- testMatchers {
+ bodyMatchers {
// asserts the jsonpath value against manual regex
jsonPath('$.duck', byRegex("[0-9]{3}"))
// asserts the jsonpath value against the provided value
@@ -7357,6 +7396,10 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
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())
@@ -7388,9 +7431,11 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
// 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}'))))
}
}
}
@@ -7414,6 +7459,7 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
valueWithTypeMatch: "string"
key:
"complex.key": 'foo'
+ nullValue: null
matchers:
headers:
- key: Content-Type
@@ -7443,6 +7489,8 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
type: by_time
- path: "$.['key'].['complex.key']"
type: by_equality
+ - path: $.nullvalue
+ type: by_null
headers:
Content-Type: application/json
response:
@@ -7473,6 +7521,7 @@ response:
valueWithMaxEmpty: []
key:
'complex.key' : 'foo'
+ nulValue: null
matchers:
headers:
- key: Content-Type
@@ -7521,6 +7570,9 @@ response:
- path: $.duck
type: by_command
value: assertThatValueIsANumber($it)
+ - path: $.nullValue
+ type: by_null
+ value: null
headers:
Content-Type: application/json
@@ -7531,7 +7583,7 @@ response:
contain are explicitly set. For the valueWithoutAMatcher, the verification takes place
in the same way as without the use of matchers. In that case, the test performs an
equality check.
-For the response side in the testMatchers section, we define the dynamic parts in a
+For the response side in the bodyMatchers section, we define the dynamic parts in a
similar manner. The only difference is that the byType matchers are also present. The
verifier engine checks four fields to verify whether the response from the test
has a value for which the JSON path matches the given field, is of the same type as the one
@@ -7558,7 +7610,7 @@ separates the autogenerated assertions and the assertion from matchers):// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/json")
- .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\"}");
+ .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\"}}");
// when:
ResponseOptions response = given().spec(request)
@@ -7569,29 +7621,30 @@ separates the autogenerated assertions and the assertion from matchers):
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).as("$.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0);
+ assertThatValueIsANumber(parsedJson.read("$.duck"));
+ assertThat(parsedJson.read("$.['key'].['complex.key']", String.class)).isEqualTo("foo");
Notice that, for the byCommand method, the example calls the
assertThatValueIsANumber. This method must be defined in the test base class or be
@@ -7652,7 +7705,7 @@ the method name and passed the proper JSON path as a parameter to it.
}
'''
-If you use a matcher, then the part of the request aned response that the
+If you use a matcher, then the part of the request and response that the
matcher addresses with the JSON Path gets removed from the assertion. In the case of
verifying a collection, you must create matchers for all the elements of the
collection.
@@ -7676,7 +7729,7 @@ collection.
]
]
)
- testMatchers {
+ bodyMatchers {
jsonPath('$.events[0].operation', byRegex('.+'))
jsonPath('$.events[0].eventId', byRegex('^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'))
jsonPath('$.events[0].status', byRegex('.+'))
@@ -7737,7 +7790,7 @@ content type set. Otherwise, the default of application/octet-streamAsync Support
If you’re using asynchronous communication on the server side (your controllers are
returning Callable, DeferredResult, and so on), then, inside your contract, you must
-provide a sync() method in the response section. The following code shows an example:
+provide an async() method in the response section. The following code shows an example:Groovy DSL
@@ -8468,8 +8521,14 @@ org.springframework.cloud.contract.verifier.converter.YamlContractConverter
Pact ConverterSpring Cloud Contract includes support for Pact representation of
-contracts. Instead of using the Groovy DSL, you can use Pact files. In this section, we
-present how to add Pact support for your project.
+contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
+present how to add Pact support for your project. Note however that not all functionality is supported.
+Starting with v3 you can combine multiple matcher for the same element;
+you can use matchers for the body, headers, request and path; and you can use value generators.
+Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
+Next to that the request and path matchers are skipped during the conversion.
+When using a date, time or datetime value generator with a given format,
+the given format will be skipped and the ISO format will be used.Pact Contract
@@ -8495,10 +8554,36 @@ present how to add Pact support for your project.
"clientId": "1234567890",
"loanAmount": 99999
},
+ "generators": {
+ "body": {
+ "$.clientId": {
+ "type": "Regex",
+ "regex": "[0-9]{10}"
+ }
+ }
+ },
"matchingRules": {
- "$.body.clientId": {
- "match": "regex",
- "regex": "[0-9]{10}"
+ "header": {
+ "Content-Type": {
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "application/vnd\\.fraud\\.v1\\+json.*"
+ }
+ ],
+ "combine": "AND"
+ }
+ },
+ "body" : {
+ "$.clientId": {
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "[0-9]{10}"
+ }
+ ],
+ "combine": "AND"
+ }
}
}
},
@@ -8512,9 +8597,27 @@ present how to add Pact support for your project.
"rejectionReason": "Amount too high"
},
"matchingRules": {
- "$.body.fraudCheckStatus": {
- "match": "regex",
- "regex": "FRAUD"
+ "header": {
+ "Content-Type": {
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "application/vnd\\.fraud\\.v1\\+json.*"
+ }
+ ],
+ "combine": "AND"
+ }
+ },
+ "body": {
+ "$.fraudCheckStatus": {
+ "matchers": [
+ {
+ "match": "regex",
+ "regex": "FRAUD"
+ }
+ ],
+ "combine": "AND"
+ }
}
}
}
@@ -8522,10 +8625,10 @@ present how to add Pact support for your project.
],
"metadata": {
"pact-specification": {
- "version": "2.0.0"
+ "version": "3.0.0"
},
"pact-jvm": {
- "version": "2.4.18"
+ "version": "3.5.13"
}
}
}
@@ -8533,7 +8636,7 @@ present how to add Pact support for your project.
Pact for Producers
-On the producer side, you mustadd two additional dependencies to your plugin
+On the producer side, you must add two additional dependencies to your plugin
configuration. One is the Spring Cloud Contract Pact support, and the other represents
the current Pact version that you use.
@@ -8556,7 +8659,7 @@ the current Pact version that you use.
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-model</artifactId>
- <version>2.4.18</version>
+ <version>3.5.13</version>
</dependency>
</dependencies>
</plugin>
@@ -8566,7 +8669,7 @@ the current Pact version that you use.
Gradleclasspath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
-classpath 'au.com.dius:pact-jvm-model:2.4.18'
+classpath 'au.com.dius:pact-jvm-model:3.5.13'
When you execute the build of your application, a test will be generated. The generated
@@ -8584,26 +8687,27 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
// then:
assertThat(response.statusCode()).isEqualTo(200);
- assertThat(response.header("Content-Type")).isEqualTo("application/vnd.fraud.v1+json;charset=UTF-8");
+ assertThat(response.header("Content-Type")).matches("application/vnd\\.fraud\\.v1\\+json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
- assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
+ assertThatJson(parsedJson).field("['rejectionReason']").isEqualTo("Amount too high");
// and:
assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
}
The corresponding generated stub might be as follows:{
+ "id" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
"uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
"request" : {
"url" : "/fraudcheck",
"method" : "PUT",
"headers" : {
"Content-Type" : {
- "equalTo" : "application/vnd.fraud.v1+json"
+ "matches" : "application/vnd\\.fraud\\.v1\\+json.*"
}
},
"bodyPatterns" : [ {
- "matchesJsonPath" : "$[?(@.loanAmount == 99999)]"
+ "matchesJsonPath" : "$[?(@.['loanAmount'] == 99999)]"
}, {
"matchesJsonPath" : "$[?(@.clientId =~ /([0-9]{10})/)]"
} ]
@@ -8613,8 +8717,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
"body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
"headers" : {
"Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
- }
- }
+ },
+ "transformers" : [ "response-template" ]
+ },
}
@@ -8633,7 +8738,7 @@ current Pact version that you use.
<dependency>
<groupId>au.com.dius</groupId>
<artifactId>pact-jvm-model</artifactId>
- <version>2.4.18</version>
+ <version>3.5.13</version>
<scope>test</scope>
</dependency>
@@ -8642,7 +8747,7 @@ current Pact version that you use.
GradletestCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
-testCompile 'au.com.dius:pact-jvm-model:2.4.18'
+testCompile 'au.com.dius:pact-jvm-model:3.5.13'