diff --git a/2.0.x/images/callouts/1.png b/2.0.x/images/callouts/1.png new file mode 100644 index 0000000000..7d473430b7 Binary files /dev/null and b/2.0.x/images/callouts/1.png differ diff --git a/2.0.x/images/callouts/2.png b/2.0.x/images/callouts/2.png new file mode 100644 index 0000000000..5d09341b2f Binary files /dev/null and b/2.0.x/images/callouts/2.png differ diff --git a/2.0.x/images/callouts/3.png b/2.0.x/images/callouts/3.png new file mode 100644 index 0000000000..ef7b700471 Binary files /dev/null and b/2.0.x/images/callouts/3.png differ diff --git a/2.0.x/multi/images/callouts/1.png b/2.0.x/multi/images/callouts/1.png new file mode 100644 index 0000000000..7d473430b7 Binary files /dev/null and b/2.0.x/multi/images/callouts/1.png differ diff --git a/2.0.x/multi/images/callouts/2.png b/2.0.x/multi/images/callouts/2.png new file mode 100644 index 0000000000..5d09341b2f Binary files /dev/null and b/2.0.x/multi/images/callouts/2.png differ diff --git a/2.0.x/multi/images/callouts/3.png b/2.0.x/multi/images/callouts/3.png new file mode 100644 index 0000000000..ef7b700471 Binary files /dev/null and b/2.0.x/multi/images/callouts/3.png differ diff --git a/2.0.x/multi/multi__contract_dsl.html b/2.0.x/multi/multi__contract_dsl.html index f500fc466e..8303a19275 100644 --- a/2.0.x/multi/multi__contract_dsl.html +++ b/2.0.x/multi/multi__contract_dsl.html @@ -1,15 +1,14 @@ - 8. Contract DSL

8. Contract DSL

[Important]Important

Remember that, inside the contract file, you have to provide the fully + 8. Contract DSL

8. Contract DSL

Spring Cloud Contract supports out of the box 2 types of DSL. One written in +Groovy and one written in YAML.

If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy +before. Knowledge of the language is not really needed, as the Contract DSL uses only a +tiny subset of it (only literals, method calls and closures). Also, the DSL is statically +typed, to make it programmer-readable without any knowledge of the DSL itself.

[Important]Important

Remember that, inside the Groovy contract file, you have to provide the fully qualified name to the Contract class and make static imports, such as org.springframework.cloud.spec.Contract.make { …​ }. You can also provide an import to the Contract class: import org.springframework.cloud.spec.Contract and then call -Contract.make { …​ }.

Contract DSL is written in Groovy, but do not be alarmed if you have not used Groovy -before. Knowledge of the language is not really needed, as the Contract DSL uses only a -tiny subset of it (only literals, method calls and closures). Also, the DSL is statically -typed, to make it programmer-readable without any knowledge of the DSL itself.

[Tip]Tip

Spring Cloud Contract supports defining multiple contracts in a single file.

The Contract is present in the spring-cloud-contract-spec module of the -Spring -Cloud Contract Verifier repository.

The following is a complete example of a contract definition:

org.springframework.cloud.contract.spec.Contract.make {
+Contract.make { …​ }.

[Tip]Tip

Spring Cloud Contract supports defining multiple contracts in a single file.

The following is a complete example of a Groovy contract definition:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'PUT'
 		url '/api/12'
@@ -48,18 +47,66 @@ Cloud Contract Verifier repository.

The following is a complete exampl ''' } response { - status 200 + status OK() } -}

[Note]Note

The preceding example does not contain all the features of the DSL appear. The -remainder of this section describes the other features.

You can compile Contracts to WireMock stubs mapping using standalone maven command: -mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert

8.1 Limitations

[Warning]Warning

Spring Cloud Contract Verifier does not properly support XML. Please use JSON or +}

The following is a complete example of a YAML contract definition:

description: Some description
+name: some name
+priority: 8
+ignored: true
+request:
+  url: /foo
+  queryParameters:
+    a: b
+    b: c
+  method: PUT
+  headers:
+    foo: bar
+    fooReq: baz
+  body:
+    foo: bar
+  matchers:
+    body:
+      - path: $.foo
+        type: by_regex
+        value: bar
+    headers:
+      - key: foo
+        regex: bar
+response:
+  status: 200
+  headers:
+    foo2: bar
+    foo3: foo33
+    fooRes: baz
+  body:
+    foo2: bar
+    foo3: baz
+    nullValue: null
+  matchers:
+    body:
+      - path: $.foo2
+        type: by_regex
+        value: bar
+      - path: $.foo3
+        type: by_command
+        value: executeMe($it)
+      - path: $.nullValue
+        type: by_null
+        value: null
+    headers:
+      - key: foo2
+        regex: bar
+      - key: foo3
+        command: andMeToo($it)
[Tip]Tip

You can compile contracts to stubs mapping using standalone maven command: +mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert

8.1 Limitations

[Warning]Warning

Spring Cloud Contract Verifier does not properly support XML. Please use JSON or help us implement this feature.

[Warning]Warning

The support for verifying the size of JSON arrays is experimental. If you want to turn it on, please set the value of the following system property to true: spring.cloud.contract.verifier.assert.size. By default, this feature is set to false. You can also provide the assertJsonSize property in the plugin configuration.

[Warning]Warning

Because JSON structure can have any form, it can be impossible to parse it -properly when using the value(consumer(…​), producer(…​)) notation in GString. That +properly when using the Groovy DSL and the value(consumer(…​), producer(…​)) notation in GString. That is why you should use the Groovy Map notation.

8.2 Common Top-Level elements

The following sections describe the most common top-level elements:

8.2.1 Description

You can add a description to your contract. The description is arbitrary text. The -following code shows an example:

		org.springframework.cloud.contract.spec.Contract.make {
+following code shows an example:

Groovy DSL.  +

		org.springframework.cloud.contract.spec.Contract.make {
 			description('''
 given:
 	An input
@@ -68,23 +115,85 @@ when:
 then:
 	Output
 ''')
-		}

8.2.2 Name

You can provide a name for your contract. Assume that you provided the following name: + }

+

YAML.  +

description: Some description
+name: some name
+priority: 8
+ignored: true
+request:
+  url: /foo
+  queryParameters:
+    a: b
+    b: c
+  method: PUT
+  headers:
+    foo: bar
+    fooReq: baz
+  body:
+    foo: bar
+  matchers:
+    body:
+      - path: $.foo
+        type: by_regex
+        value: bar
+    headers:
+      - key: foo
+        regex: bar
+response:
+  status: 200
+  headers:
+    foo2: bar
+    foo3: foo33
+    fooRes: baz
+  body:
+    foo2: bar
+    foo3: baz
+    nullValue: null
+  matchers:
+    body:
+      - path: $.foo2
+        type: by_regex
+        value: bar
+      - path: $.foo3
+        type: by_command
+        value: executeMe($it)
+      - path: $.nullValue
+        type: by_null
+        value: null
+    headers:
+      - key: foo2
+        regex: bar
+      - key: foo3
+        command: andMeToo($it)

+

8.2.2 Name

You can provide a name for your contract. Assume that you provided the following name: should register a user. If you do so, the name of the autogenerated test is validate_should_register_a_user. Also, the name of the stub in a WireMock stub is should_register_a_user.json.

[Important]Important

You must ensure that the name does not contain any characters that make the generated test not compile. Also, remember that, if you provide the same name for multiple contracts, your autogenerated tests fail to compile and your generated stubs -override each other.

8.2.3 Ignoring Contracts

If you want to ignore a contract, you can either set a value of ignored contracts in the -plugin configuration or set the ignored property on the contract itself:

org.springframework.cloud.contract.spec.Contract.make {
+override each other.

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
+	name("some_special_name")
+}

+

YAML.  +

name: some name

+

8.2.3 Ignoring Contracts

If you want to ignore a contract, you can either set a value of ignored contracts in the +plugin configuration or set the ignored property on the contract itself:

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	ignored()
-}

8.2.4 Passing Values from Files

Starting with version 1.2.0, you can pass values from files. Assume that you have the +}

+

YAML.  +

ignored: true

+

8.2.4 Passing Values from Files

Starting with version 1.2.0, you can pass values from files. Assume that you have the following resources in our project.

└── src
     └── test
         └── resources
             └── contracts
                 ├── readFromFile.groovy
                 ├── request.json
-                └── response.json

Further assume that your contract is as follows:

import org.springframework.cloud.contract.spec.Contract
+                └── response.json

Further assume that your contract is as follows:

Groovy DSL.  +

import org.springframework.cloud.contract.spec.Contract
 
 Contract.make {
 	request {
@@ -96,17 +205,26 @@ Contract.make {
 		url("/1")
 	}
 	response {
-		status 200
+		status OK()
 		body(file("response.json"))
 		headers {
 			contentType(textPlain())
 		}
 	}
-}

Further assume that the JSON files is as follows:

request.json

{ "status" : "REQUEST" }

response.json

{ "status" : "RESPONSE" }

When test or stub generation takes place, the contents of the file is passed to the body -of a request or a response. That works because of the file(…​) method. The argument of -that method needs to be a file with location relative to the folder in which the contract -lays.

8.2.5 HTTP Top-Level Elements

The following methods can be called in the top-level closure of a contract definition. -request and response are mandatory. priority is optional.

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

request:
+  method: GET
+  url: /foo
+  bodyFromFile: request.json
+response:
+  status: 200
+  bodyFromFile: response.json

+

Further assume that the JSON files is as follows:

request.json

{ "status" : "REQUEST" }

response.json

{ "status" : "RESPONSE" }

When test or stub generation takes place, the contents of the file is passed to the body +of a request or a response. The name of the file needs to be a file with location +relative to the folder in which the contract lays.

8.2.5 HTTP Top-Level Elements

The following methods can be called in the top-level closure of a contract definition. +request and response are mandatory. priority is optional.

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	// Definition of HTTP request part of the contract
 	// (this can be a valid request or invalid depending
 	// on type of contract being specified).
@@ -125,8 +243,18 @@ lays.

// Contract priority, which can be used for overriding // contracts (1 is highest). Priority is optional. priority 1 -}

8.3 Request

The HTTP protocol requires only method and address to be specified in a request. The -same information is mandatory in request definition of the Contract.

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

priority: 8
+request:
+...
+response:
+...

+

[Important]Important

If you want to make your contract have a higher value of priority +you need to pass a lower number to the priority tag / method. E.g. priority with +value 5 has higher priority than priority with value 10.

8.3 Request

The HTTP protocol requires only method and url to be specified in a request. The +same information is mandatory in request definition of the Contract.

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		// HTTP request method (GET/POST/PUT/DELETE).
 		method 'GET'
@@ -138,8 +266,13 @@ same information is mandatory in request definition of the Contract.

//...
 	}
-}

It is possible to specify an absolute rather than relative url, but using urlPath is -the recommended way, as doing so makes the tests host-independent.

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

method: PUT
+url: /foo

+

It is possible to specify an absolute rather than relative url, but using urlPath is +the recommended way, as doing so makes the tests host-independent.

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'GET'
 
@@ -150,8 +283,13 @@ the recommended way, as doing so makes the tests ho
 	response {
 		//...
 	}
-}

request may contain query parameters, which are specified in a closure nested in a -call to urlPath or url.

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

request:
+  method: PUT
+  urlPath: /foo

+

request may contain query parameters.

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -191,7 +329,62 @@ call to urlPath or url
 	response {
 		//...
 	}
-}

request may contain additional request headers, as shown in the following example:

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

request:
+...
+  queryParameters:
+    a: b
+    b: c
+  headers:
+    foo: bar
+    fooReq: baz
+  cookies:
+    foo: bar
+    fooReq: baz
+  body:
+    foo: bar
+  matchers:
+    body:
+      - path: $.foo
+        type: by_regex
+        value: bar
+    headers:
+      - key: foo
+        regex: bar
+response:
+  status: 200
+  headers:
+    foo2: bar
+    foo3: foo33
+    fooRes: baz
+  body:
+    foo2: bar
+    foo3: baz
+    nullValue: null
+  matchers:
+    body:
+      - path: $.foo2
+        type: by_regex
+        value: bar
+      - path: $.foo3
+        type: by_command
+        value: executeMe($it)
+      - path: $.nullValue
+        type: by_null
+        value: null
+    headers:
+      - key: foo2
+        regex: bar
+      - key: foo3
+        command: andMeToo($it)
+    cookies:
+      - key: foo2
+        regex: bar
+      - key: foo3
+        predefined:

+

request may contain additional request headers, as shown in the following example:

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -208,7 +401,40 @@ call to urlPath or url
 	response {
 		//...
 	}
-}

request may contain a request body, as shown in the following example:

org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

request:
+...
+headers:
+  foo: bar
+  fooReq: baz

+

request may contain additional request cookies, as shown in the following example:

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
+	request {
+		//...
+
+		// Each Cookies is added in form `'Cookie-Key' : 'Cookie-Value'`.
+		// there are also some helper methods
+		cookies {
+			cookie 'key': 'value'
+			cookie('another_key', 'another_value')
+		}
+
+		//...
+	}
+
+	response {
+		//...
+	}
+}

+

YAML.  +

request:
+...
+cookies:
+  foo: bar
+  fooReq: baz

+

request may contain a request body:

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -220,8 +446,15 @@ call to urlPath or url
 	response {
 		//...
 	}
-}

request may contain multipart elements. To include multipart elements, call the -multipart() method, as shown in the following example

org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
+}

+

YAML.  +

request:
+...
+body:
+  foo: bar

+

request may contain multipart elements. To include multipart elements, use the +multipart method/section, as shown in the following examples

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method "PUT"
 		url "/multipart"
@@ -238,17 +471,75 @@ call to urlPath or url
 						// name of the file
 						name: $(c(regex(nonEmpty())), p('filename.csv')),
 						// content of the file
-						content: $(c(regex(nonEmpty())), p('file content')))
+						content: $(c(regex(nonEmpty())), p('file content')),
+						// content type for the part
+						contentType: $(c(regex(nonEmpty())), p('application/json')))
+		)
+	}
+	response {
+		status OK()
+	}
+}
+org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
+	request {
+		method "PUT"
+		url "/multipart"
+		headers {
+			contentType('multipart/form-data;boundary=AaB03x')
+		}
+		multipart(
+				file: named(
+						name: value(stub(regex('.+')), test('file')),
+						content: value(stub(regex('.+')), test([100, 117, 100, 97] as byte[]))
+				)
 		)
 	}
 	response {
 		status 200
 	}
-}

In the preceding example, we define parameters in either of two ways:

  • Directly, by using the map notation, where the value can be a dynamic property (such as +}

+

YAML.  +

request:
+  method: PUT
+  url: /multipart
+  headers:
+    Content-Type: multipart/form-data;boundary=AaB03x
+  multipart:
+    params:
+    # key (parameter name), value (parameter value) pair
+      formParameter: '"formParameterValue"'
+      someBooleanParameter: true
+    named:
+      - paramName: file
+        fileName: filename.csv
+        fileContent: file content
+  matchers:
+    multipart:
+      params:
+        - key: formParameter
+          regex: ".+"
+        - key: someBooleanParameter
+          predefined: any_boolean
+      named:
+        - paramName: file
+          fileName:
+            predefined: non_empty
+          fileContent:
+            predefined: non_empty
+response:
+  status: 200

+

In the preceding example, we define parameters in either of two ways:

Groovy DSL

  • Directly, by using the map notation, where the value can be a dynamic property (such as formParameter: $(consumer(…​), producer(…​))).
  • By using the named(…​) method that lets you set a named parameter. A named parameter can set a name and content. You can call it either via a method with two arguments, such as named("fileName", "fileContent"), or via a map notation, such as -named(name: "fileName", content: "fileContent").

From this contract, the generated test is as follows:

// given:
+named(name: "fileName", content: "fileContent").

YAML

  • The multipart parameters are set via multipart.params section
  • The named parameters (the fileName and fileContent for a given parameter name) +can be set via the multipart.named section. That section contains +the paramName (name of the parameter), fileName (name of the file), +fileContent (content of the file) fields
  • The dynamic bits can be set via the matchers.multipart section

    • for parameters use the params section that can accept +regex or a predefined regular expression
    • for named params use the named section where first you +define the parameter name via paramName and then you can pass the +parametrization of either fileName or fileContent via +regex or a predefined regular expression

From this contract, the generated test is as follows:

// given:
  MockMvcRequestSpecification request = given()
    .header("Content-Type", "multipart/form-data;boundary=AaB03x")
    .param("formParameter", "\"formParameterValue\"")
@@ -271,11 +562,11 @@ such as named("fileName", "fileContent"), or via a
 	  }
 	},
 	"bodyPatterns" : [ {
-		"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"formParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\".+\\"\\r\\n--\\\\1.*"
+		"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"formParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\".+\\"\\r\\n--\\\\1.*"
   		}, {
-    			"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"someBooleanParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"
+    			"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"someBooleanParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"
   		}, {
-	  "matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"file\\"; filename=\\"[\\\\S\\\\s]+\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n[\\\\S\\\\s]+\\r\\n--\\\\1.*"
+	  "matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"file\\"; filename=\\"[\\\\S\\\\s]+\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n[\\\\S\\\\s]+\\r\\n--\\\\1.*"
 	} ]
   },
   "response" : {
@@ -284,21 +575,31 @@ such as named("fileName", "fileContent"), or via a
   }
 }
 	'''

8.4 Response

The response must contain an HTTP status code and may contain other information. The -following code shows an example:

org.springframework.cloud.contract.spec.Contract.make {
+following code shows an example:

Groovy DSL.  +

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 	}
 	response {
 		// Status code sent by the server
 		// in response to request specified above.
-		status 200
+		status OK()
 	}
-}

Besides status, the response may contain headers and a body, both of which are -specified the same way as in the request (see the previous paragraph).

8.5 Dynamic properties

The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not +}

+

YAML.  +

response:
+...
+status: 200

+

Besides status, the response may contain headers, cookies and a body, both of which are +specified the same way as in the request (see the previous paragraph).

[Tip]Tip

Via the Groovy DSL you can reference the org.springframework.cloud.contract.spec.internal.HttpStatus +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. 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.

8.5.1 Dynamic properties inside the body

You can set the properties inside the body either with the value method or, if you use +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 a separate section called +bodyMatchers.

[Note]Note

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.

8.5.1 Dynamic properties inside the body

[Important]Important

This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

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(...))
 value(c(...), p(...))
@@ -307,7 +608,8 @@ value(client(...), server(...))

The following example shows how to set d $(c(...), p(...)) $(stub(...), test(...)) $(client(...), server(...))

Both approaches work equally well. stub and client methods are aliases over the consumer -method. Subsequent sections take a closer look at what you can do with those values.

8.5.2 Regular expressions

You can use regular expressions to write your requests in Contract DSL. Doing so is +method. Subsequent sections take a closer look at what you can do with those values.

8.5.2 Regular expressions

[Important]Important

This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

You can use regular expressions to write your requests in Contract DSL. Doing so is particularly useful when you want to indicate that a given response should be provided for requests that follow a given pattern. Also, you can use regular expressions when you need to use patterns and not exact values both for your test and your server side tests.

The following example shows how to use regular expressions to write a request:

org.springframework.cloud.contract.spec.Contract.make {
@@ -316,7 +618,7 @@ need to use patterns and not exact values both for your test and your server sid
 		url $(consumer(~/\/[0-9]{2}/), producer('/12'))
 	}
 	response {
-		status 200
+		status OK()
 		body(
 				id: $(anyNumber()),
 				surname: $(
@@ -347,7 +649,7 @@ the provided regular expression. The following code shows an example:

200
+		status OK()
 		body([
 			responseElement: $(producer(regex('[0-9]{7}')))
 		])
@@ -358,12 +660,18 @@ 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:

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+)?')
+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}')
 protected static final Pattern URL = UrlHelper.URL
+protected static final Pattern HTTPS_URL = UrlHelper.HTTPS_URL
 protected static final Pattern UUID = Pattern.compile('[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}')
 protected static final Pattern ANY_DATE = Pattern.compile('(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])')
 protected static final Pattern ANY_DATE_TIME = Pattern.compile('([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])')
@@ -380,14 +688,30 @@ Pattern onlyAlphaUnicode() {
 	return ONLY_ALPHA_UNICODE
 }
 
+Pattern alphaNumeric() {
+	return ALPHA_NUMERIC
+}
+
 Pattern number() {
 	return NUMBER
 }
 
+Pattern positiveInt() {
+	return POSITIVE_INT
+}
+
 Pattern anyBoolean() {
 	return TRUE_OR_FALSE
 }
 
+Pattern anInteger() {
+	return INTEGER
+}
+
+Pattern aDouble() {
+	return DOUBLE
+}
+
 Pattern ipAddress() {
 	return IP_ADDRESS
 }
@@ -404,6 +728,10 @@ Pattern url() {
 	return URL
 }
 
+Pattern httpsUrl() {
+	return HTTPS_URL
+}
+
 Pattern uuid(){
 	return UUID
 }
@@ -453,7 +781,8 @@ Pattern nonBlank() {
 				message: "User not found by email = [${value(producer(regex(email())), consumer('not.existing@user.com'))}]"
 		)
 	}
-}

8.5.3 Passing Optional Parameters

It is possible to provide optional parameters in your contract. However, you can provide +}

8.5.3 Passing Optional Parameters

[Important]Important

This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

It is possible to provide optional parameters in your contract. However, you can provide optional parameters only for the following:

  • STUB side of the Request
  • TEST side of the Response

The following example shows how to provide optional parameters:

org.springframework.cloud.contract.spec.Contract.make {
 	priority 1
 	request {
@@ -496,29 +825,30 @@ expression that must be present 0 or more times.

If you use Spock for, the """

The following stub would also be generated:

'''
 {
   "request" : {
-    "url" : "/users/password",
-    "method" : "POST",
-    "bodyPatterns" : [ {
-      "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
-    } ],
-    "headers" : {
-      "Content-Type" : {
-        "equalTo" : "application/json"
-      }
-    }
+	"url" : "/users/password",
+	"method" : "POST",
+	"bodyPatterns" : [ {
+	  "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
+	} ],
+	"headers" : {
+	  "Content-Type" : {
+		"equalTo" : "application/json"
+	  }
+	}
   },
   "response" : {
-    "status" : 404,
-    "body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}",
-    "headers" : {
-      "Content-Type" : "application/json"
-    }
+	"status" : 404,
+	"body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}",
+	"headers" : {
+	  "Content-Type" : "application/json"
+	}
   },
   "priority" : 1
 }
-'''

8.5.4 Executing Custom Methods on the Server Side

You can define a method call that executes on the server side during the test. Such a +'''

8.5.4 Executing Custom Methods on the Server Side

[Important]Important

This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

You can define a method call that executes on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" in the configuration. The following code shows an example of the contract portion of the test case:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
@@ -538,7 +868,7 @@ following code shows an example of the contract portion of the test case:

'/api/12'), producer(regex('^/api/[0-9]{2}$'))), correlationId: $(consumer('1223456'), producer(execute('isProperCorrelationId($it)'))) ) - status 200 + status OK() } }

The following code shows the base class portion of the test case:

abstract class BaseMockMvcSpec extends Specification {
 
@@ -569,7 +899,7 @@ is applied for the whole body - not for parts of it.

) } response { - status 200 + status OK() } }

The preceding example results in calling the hashCode() method in the request body. It should resemble the following code:

// given:
@@ -582,11 +912,17 @@ It should resemble the following code:

// then:
  assertThat(response.statusCode()).isEqualTo(200);

8.5.5 Referencing the Request from the Response

The best situation is to provide fixed values, but sometimes you need to reference a -request in your response. To do so, you can use the fromRequest() method, which lets +request in your response.

If you’re writing contracts using Groovy DSL, you can use the fromRequest() method, which lets you reference a bunch of elements from the HTTP request. You can use the following options:

  • fromRequest().url(): Returns the request URL and query parameters.
  • fromRequest().query(String key): Returns the first query parameter with a given name.
  • fromRequest().query(String key, int index): Returns the nth query parameter with a given name.
  • fromRequest().path(): Returns the full path.
  • fromRequest().path(int index): Returns the nth path element.
  • fromRequest().header(String key): Returns the first header with a given name.
  • fromRequest().header(String key, int index): Returns the nth header with a given name.
  • fromRequest().body(): Returns the full request body.
  • fromRequest().body(String jsonPath): Returns the element from the request that -matches the JSON Path.

Consider the following contract:

Contract contractDsl = Contract.make {
+matches the JSON Path.

If you’re using the YAML contract definition you have to use the +Handlebars {{{ }}} notation with custom, Spring Cloud Contract + functions to achieve this.

  • {{{ request.url }}}: Returns the request URL and query parameters.
  • {{{ request.query.key.[index] }}}: Returns the nth query parameter with a given name. +E.g. for key foo, first entry {{{ request.query.foo.[0] }}}
  • {{{ request.path }}}: Returns the full path.
  • {{{ request.path.[index] }}}: Returns the nth path element. E.g. +for first entry `{{{ request.path.[0] }}}
  • {{{ request.headers.key }}}: Returns the first header with a given name.
  • {{{ request.headers.key.[index] }}}: Returns the nth header with a given name.
  • {{{ request.body }}}: Returns the full request body.
  • {{{ jsonpath this 'your.json.path' }}}: Returns the element from the request that +matches the JSON Path. E.g. for json path $.foo - {{{ jsonpath this '$.foo' }}}

Consider the following contract:

Groovy DSL.  +

Contract contractDsl = Contract.make {
 	request {
 		method 'GET'
 		url('/api/v1/xxxx') {
@@ -602,7 +938,7 @@ matches the JSON Path.

Consider the following contract:

"bar", baz: 5) } response { - status 200 + status OK() headers { header(authorization(), "foo ${fromRequest().header(authorization())} bar") } @@ -620,7 +956,39 @@ matches the JSON Path.

Consider the following contract:

"Bla bla ${fromRequest().body('$.foo')} bla bla" ) } -}

Running a JUnit test generation leads to a test that resembles the following example:

// given:
+}

+

YAML.  +

request:
+  method: GET
+  url: /api/v1/xxxx
+  queryParameters:
+    foo:
+      - bar
+      - bar2
+  headers:
+    Authorization:
+      - secret
+      - secret2
+  body:
+    foo: bar
+    baz: 5
+response:
+  status: 200
+  headers:
+    Authorization: "foo {{{ request.headers.Authorization.0 }}} bar"
+  body:
+    url: "{{{ request.url }}}"
+    path: "{{{ request.path }}}"
+    pathIndex: "{{{ request.path.1 }}}"
+    param: "{{{ request.query.foo }}}"
+    paramIndex: "{{{ request.query.foo.1 }}}"
+    authorization: "{{{ request.headers.Authorization.0 }}}"
+    authorization2: "{{{ request.headers.Authorization.1 }}"
+    fullBody: "{{{ request.body }}}"
+    responseFoo: "{{{ jsonpath this '$.foo' }}}"
+    responseBaz: "{{{ jsonpath this '$.baz' }}}"
+    responseBaz2: "Bla bla {{{ jsonpath this '$.foo' }}} bla bla"

+

Running a JUnit test generation leads to a test that resembles the following example:

// given:
  MockMvcRequestSpecification request = given()
    .header("Authorization", "secret")
    .header("Authorization", "secret2")
@@ -697,7 +1065,9 @@ provide your own extensions, you can register an implementation of the
 org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions interface.
 Since we use the spring.factories extension approach, you can create an entry in
 META-INF/spring.factories file similar to the following:

org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\
-org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions

The following is an example of a custom extension:

TestWireMockExtensions.groovy.  +org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions +org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.stubrunner.TestCustomYamlContractConverter

The following is an example of a custom extension:

TestWireMockExtensions.groovy. 

package org.springframework.cloud.contract.verifier.dsl.wiremock
 
 import com.github.tomakehurst.wiremock.extension.Extension
@@ -723,30 +1093,39 @@ org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExte
 	}
 }

[Important]Important

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. +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:

    • For stubMatchers:

      • 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 -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 -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.
      • byType(): The value taken from the response via the provided JSON Path needs to be +following matching possibilities:

        Groovy DSL

        • For the stubs(in tests on the Consumer’s side):

          • byEquality(): The value taken from the consumer’s request via the provided JSON Path must be +equal to the value provided in the contract.
          • byRegex(…​): The value taken from the consumer’s request via the provided JSON Path must +match the regex.
          • byDate(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO Date value.
          • byTimestamp(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO DateTime value.
          • byTime(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO Time value.
        • For the verification(in generated tests on the Producer’s side):

          • byEquality(): The value taken from the producer’s response via the provided JSON Path must be +equal to the provided value in the contract.
          • byRegex(…​): The value taken from the producer’s response via the provided JSON Path must +match the regex.
          • byDate(): The value taken from the producer’s response via the provided JSON Path must match +the regex for an ISO Date value.
          • byTimestamp(): The value taken from the producer’s response via the provided JSON Path must +match the regex for an ISO DateTime value.
          • byTime(): The value taken from the producer’s response via the provided JSON Path must match +the regex for an ISO Time value.
          • byType(): The value taken from the producer’s response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. byType can take a closure, in which you can set minOccurrence and maxOccurrence. That way, you can assert the size of the flattened collection. To check the size of an -unflattened collection, use a custom method with the byCommand(…​) testMatcher.
          • byCommand(…​): The value taken from the response via the provided JSON Path is +unflattened collection, use a custom method with the byCommand(…​) testMatcher.

          • byCommand(…​): The value taken from the producer’s response via the provided JSON Path is 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.

        Consider the following example:

        Contract contractDsl = Contract.make {
        +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]:

    - path: $.foo
    +  type: by_regex
    +  predefined: only_alpha_unicode

    Below you can find the allowed list of `type`s.

    • For stubMatchers:

      • by_equality
      • by_regex
      • by_date
      • by_timestamp
      • by_time
    • For testMatchers:

      • by_equality
      • by_regex
      • by_date
      • by_timestamp
      • by_time
      • by_type

        • there are 2 additional fields accepted: minOccurrence and maxOccurrence.
      • by_command
      • by_null

    Consider the following example:

    Groovy DSL.  +

    Contract contractDsl = Contract.make {
     	request {
     		method 'GET'
     		urlPath '/get'
    @@ -764,7 +1143,7 @@ following, depending on the JSON path:

      'complex.key' : 'foo' ] ]) - stubMatchers { + bodyMatchers { jsonPath('$.duck', byRegex("[0-9]{3}")) jsonPath('$.duck', byEquality()) jsonPath('$.alpha', byRegex(onlyAlphaUnicode())) @@ -781,11 +1160,15 @@ following, depending on the JSON path:

        200 + 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", @@ -805,9 +1188,10 @@ following, depending on the JSON path:

          '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 @@ -816,6 +1200,10 @@ following, depending on the JSON path:

            '$.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()) @@ -847,17 +1235,152 @@ following, depending on the JSON path:

              // 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}')))) } } -}

    In the preceding example, you can see the dynamic portions of the contract in the +}

    +

    YAML.  +

    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'
    +    nullValue: null
    +  matchers:
    +    headers:
    +      - key: Content-Type
    +        regex: "application/json.*"
    +    body:
    +      - path: $.duck
    +        type: by_regex
    +        value: "[0-9]{3}"
    +      - path: $.duck
    +        type: by_equality
    +      - path: $.alpha
    +        type: by_regex
    +        predefined: only_alpha_unicode
    +      - path: $.alpha
    +        type: by_equality
    +      - path: $.number
    +        type: by_regex
    +        predefined: number
    +      - path: $.aBoolean
    +        type: by_regex
    +        predefined: any_boolean
    +      - path: $.date
    +        type: by_date
    +      - path: $.dateTime
    +        type: by_timestamp
    +      - path: $.time
    +        type: by_time
    +      - path: "$.['key'].['complex.key']"
    +        type: by_equality
    +      - path: $.nullvalue
    +        type: by_null
    +  headers:
    +    Content-Type: application/json
    +response:
    +  status: 200
    +  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"
    +    valueWithMin:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinEmpty: []
    +    valueWithMaxEmpty: []
    +    key:
    +      'complex.key' : 'foo'
    +    nulValue: null
    +  matchers:
    +    headers:
    +      - key: Content-Type
    +        regex: "application/json.*"
    +    body:
    +      - path: $.duck
    +        type: by_regex
    +        value: "[0-9]{3}"
    +      - path: $.duck
    +        type: by_equality
    +      - path: $.alpha
    +        type: by_regex
    +        predefined: only_alpha_unicode
    +      - path: $.alpha
    +        type: by_equality
    +      - path: $.number
    +        type: by_regex
    +        predefined: number
    +      - path: $.aBoolean
    +        type: by_regex
    +        predefined: any_boolean
    +      - path: $.date
    +        type: by_date
    +      - path: $.dateTime
    +        type: by_timestamp
    +      - path: $.time
    +        type: by_time
    +      - path: $.valueWithTypeMatch
    +        type: by_type
    +      - path: $.valueWithMin
    +        type: by_type
    +        minOccurrence: 1
    +      - path: $.valueWithMax
    +        type: by_type
    +        maxOccurrence: 3
    +      - path: $.valueWithMinMax
    +        type: by_type
    +        minOccurrence: 1
    +        maxOccurrence: 3
    +      - path: $.valueWithMinEmpty
    +        type: by_type
    +        minOccurrence: 0
    +      - path: $.valueWithMaxEmpty
    +        type: by_type
    +        maxOccurrence: 0
    +      - path: $.duck
    +        type: by_command
    +        value: assertThatValueIsANumber($it)
    +      - path: $.nullValue
    +        type: by_null
    +        value: null
    +  headers:
    +    Content-Type: application/json

    +

    In the preceding example, you can see the dynamic portions of the contract in the matchers sections. For the request part, you can see that, for all fields but 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 @@ -868,7 +1391,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)
@@ -879,83 +1402,84 @@ separates the autogenerated assertions and the assertion from matchers):

"Content-Type")).matches("application/json.*");
 // and:
  DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
- assertThatJson(parsedJson).field("valueWithoutAMatcher").isEqualTo("foo");
+ assertThatJson(parsedJson).field("['valueWithoutAMatcher']").isEqualTo("foo");
 // and:
  assertThat(parsedJson.read("$.duck", String.class)).matches("[0-9]{3}");
  assertThat(parsedJson.read("$.duck", Integer.class)).isEqualTo(123);
  assertThat(parsedJson.read("$.alpha", String.class)).matches("[\\p{L}]*");
  assertThat(parsedJson.read("$.alpha", String.class)).isEqualTo("abc");
- assertThat(parsedJson.read("$.number", String.class)).matches("-?\\d*(\\.\\d+)?");
+ assertThat(parsedJson.read("$.number", String.class)).matches("-?(\\d*\\.\\d+|\\d+)");
  assertThat(parsedJson.read("$.aBoolean", String.class)).matches("(true|false)");
  assertThat(parsedJson.read("$.date", String.class)).matches("(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])");
  assertThat(parsedJson.read("$.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])");
  assertThat(parsedJson.read("$.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
  assertThat((Object) parsedJson.read("$.valueWithTypeMatch")).isInstanceOf(java.lang.String.class);
  assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
- assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).hasSizeGreaterThanOrEqualTo(1);
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).as("$.valueWithMin").hasSizeGreaterThanOrEqualTo(1);
  assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
- assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).hasSizeLessThanOrEqualTo(3);
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).as("$.valueWithMax").hasSizeLessThanOrEqualTo(3);
  assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
- assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).hasSizeBetween(1, 3);
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).as("$.valueWithMinMax").hasSizeBetween(1, 3);
  assertThat((Object) parsedJson.read("$.valueWithMinEmpty")).isInstanceOf(java.util.List.class);
- assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).hasSizeGreaterThanOrEqualTo(0);
+ assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).as("$.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0);
  assertThat((Object) parsedJson.read("$.valueWithMaxEmpty")).isInstanceOf(java.util.List.class);
- assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).hasSizeLessThanOrEqualTo(0);
- assertThatValueIsANumber(parsedJson.read("$.duck"));
[Important]Important

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]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 the method name and passed the proper JSON path as a parameter to it.

The resulting WireMock stub is in the following example:

				'''
 {
   "request" : {
-    "urlPath" : "/get",
-    "method" : "POST",
-    "headers" : {
-      "Content-Type" : {
-        "matches" : "application/json.*"
-      }
-    },
-    "bodyPatterns" : [ {
-      "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]"
-    }, {
-      "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]"
-    }, {
-      "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"
-    }, {
-      "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"
-    }, {
-      "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"
-    }, {
-      "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.duck == 123)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.alpha == 'abc')]"
-    }, {
-      "matchesJsonPath" : "$[?(@.number =~ /(-?\\\\d*(\\\\.\\\\d+)?)/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.dateTime =~ /(([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]))/)]"
-    }, {
-      "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
-    }, {
-      "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]"
-    } ]
+	"urlPath" : "/get",
+	"method" : "POST",
+	"headers" : {
+	  "Content-Type" : {
+		"matches" : "application/json.*"
+	  }
+	},
+	"bodyPatterns" : [ {
+	  "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]"
+	}, {
+	  "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"
+	}, {
+	  "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"
+	}, {
+	  "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.duck == 123)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.alpha == 'abc')]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.dateTime =~ /(([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]))/)]"
+	}, {
+	  "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
+	}, {
+	  "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]"
+	} ]
   },
   "response" : {
-    "status" : 200,
-    "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}",
-    "headers" : {
-      "Content-Type" : "application/json"
-    }
+	"status" : 200,
+	"body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}",
+	"headers" : {
+	  "Content-Type" : "application/json"
+	}
   }
 }
-'''
[Important]Important

If you use a matcher, then the part of the request aned response that the +'''

[Important]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 {
@@ -964,7 +1488,7 @@ collection.

Consider the following example:

"/foo") } response { - status 200 + status OK() body(events: [[ operation : 'EXPORT', eventId : '16f1ed75-0bcc-4f0d-a04d-3121798faf99', @@ -976,7 +1500,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('.+')) @@ -1021,17 +1545,22 @@ 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:

org.springframework.cloud.contract.spec.Contract.make {
+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()
         url '/get'
     }
     response {
-        status 200
+        status OK()
         body 'Passed'
         async()
     }
-}

8.8 Working with Context Paths

Spring Cloud Contract supports context paths.

[Important]Important

The only change needed to fully support context paths is the switch on the +}

+

YAML.  +

response:
+    async: true

+

8.8 Working with Context Paths

Spring Cloud Contract supports context paths.

[Important]Important

The only change needed to fully support context paths is the switch on the PRODUCER side. Also, the autogenerated tests must use EXPLICIT mode. The consumer side remains untouched. In order for the generated test to pass, you must use EXPLICIT mode.

Maven.  @@ -1056,7 +1585,7 @@ socket.

Consider the following contract:

or
 		url '/my-context-path/url'
 	}
 	response {
-		status 200
+		status OK()
 	}
 }

The following example shows how to set up a base class and Rest Assured:

import io.restassured.RestAssured;
 import org.junit.Before;
@@ -1075,9 +1604,36 @@ socket.

Consider the following contract:

or
 	}
 }

If you do it this way:

  • All of your requests in the autogenerated tests are sent to the real endpoint with your context path included (for example, /my-context-path/url).
  • Your contracts reflect that you have a context path. Your generated stubs also have -that information (for example, in the stubs, you have to call /my-context-path/url).

8.9 Messaging Top-Level Elements

The DSL for messaging looks a little bit different than the one that focuses on HTTP. The -following sections explain the differences:

8.9.1 Output Triggered by a Method

The output message can be triggered by calling a method (such as a Scheduler when a was -started and a message was sent), as shown in the following example:

def dsl = Contract.make {
+that information (for example, in the stubs, you have to call /my-context-path/url).

8.9 Working with Web Flux

Spring Cloud Contract requires the usage of EXPLICIT mode in your generated tests +to work with Web Flux.

Maven.  +

<plugin>
+    <groupId>org.springframework.cloud</groupId>
+    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+    <version>${spring-cloud-contract.version}</version>
+    <extensions>true</extensions>
+    <configuration>
+        <testMode>EXPLICIT</testMode>
+    </configuration>
+</plugin>

+

Gradle.  +

contracts {
+		testMode = 'EXPLICIT'
+}

+

The following example shows how to set up a base class and Rest Assured for Web Flux:

@RunWith(SpringRunner.class)
+@SpringBootTest(classes = BeerRestBase.Config.class,
+		webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
+		properties = "server.port=0")
+public abstract class BeerRestBase {
+
+    // your tests go here
+
+    // in this config class you define all controllers and mocked services
+    include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=config,indent=0]
+
+}

8.10 Messaging Top-Level Elements

The DSL for messaging looks a little bit different than the one that focuses on HTTP. The +following sections explain the differences:

8.10.1 Output Triggered by a Method

The output message can be triggered by calling a method (such as a Scheduler when a was +started and a message was sent), as shown in the following example:

Groovy DSL.  +

def dsl = Contract.make {
 	// Human readable description
 	description 'Some description'
 	// Label by means of which the output message can be triggered
@@ -1098,11 +1654,31 @@ started and a message was sent), as shown in the following example:

'BOOK-NAME', 'foo')
 		}
 	}
-}

In the previous example case, the output message is sent to output if a method called +}

+

YAML.  +

# Human readable description
+description: Some description
+# Label by means of which the output message can be triggered
+label: some_label
+input:
+  # the contract will be triggered by a method
+  triggeredBy: bookReturnedTriggered()
+# output message of the contract
+outputMessage:
+  # destination to which the output message will be sent
+  sentTo: output
+  # the body of the output message
+  body:
+    bookName: foo
+  # the headers of the output message
+  headers:
+    BOOK-NAME: foo

+

In the previous example case, the output message is sent to output if a method called bookReturnedTriggered is executed. On the message publisher’s side, we generate a test that calls that method to trigger the message. On the consumer side, you can use -the some_label to trigger the message.

8.9.2 Output Triggered by a Message

The output message can be triggered by receiving a message, as shown in the following -example:

def dsl = Contract.make {
+the some_label to trigger the message.

8.10.2 Output Triggered by a Message

The output message can be triggered by receiving a message, as shown in the following +example:

Groovy DSL.  +

def dsl = Contract.make {
 	description 'Some Description'
 	label 'some_label'
 	// input is a message
@@ -1127,11 +1703,36 @@ example:

def dsl = Contract.make {
 			header('BOOK-NAME', 'foo')
 		}
 	}
-}

In the preceding example, the output message is sent to output if a proper message is +}

+

YAML.  +

# Human readable description
+description: Some description
+# Label by means of which the output message can be triggered
+label: some_label
+# input is a message
+input:
+  messageFrom: input
+  # has the following body
+  messageBody:
+    bookName: 'foo'
+  # and the following headers
+  messageHeaders:
+    sample: 'header'
+# output message of the contract
+outputMessage:
+  # destination to which the output message will be sent
+  sentTo: output
+  # the body of the output message
+  body:
+    bookName: foo
+  # the headers of the output message
+  headers:
+    BOOK-NAME: foo

+

In the preceding example, the output message is sent to output if a proper message is received on the input destination. On the message publisher’s side, the engine generates a test that sends the input message to the defined destination. On the consumer side, you can either send a message to the input destination or use a label -(some_label in the example) to trigger the message.

8.9.3 Consumer/Producer

In HTTP, you have a notion of client/stub and `server/test notation. You can also +(some_label in the example) to trigger the message.

8.10.3 Consumer/Producer

[Important]Important

This section is valid only for Groovy DSL.

In HTTP, you have a notion of client/stub and `server/test notation. You can also use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also provides the consumer and producer methods, as presented in the following example (note that you can use either $ or value methods to provide consumer and producer @@ -1152,11 +1753,12 @@ parts):

Contract.make {
 				bookName: 'foo'
 		])
 	}
-}

8.9.4 Common

In the input {} or outputMessage {} section you can call assertThat with the name +}

8.10.4 Common

In the input or outputMessage section you can call assertThat with the name of a method (e.g. assertThatMessageIsOnTheQueue()) that you have defined in the -base class or in a static import. Spring Cloud Pipelines will execute that method -in the genertaed test.

8.10 Multiple Contracts in One File

You can define multiple contracts in one file. Such a contract might resemble the -following example:

import org.springframework.cloud.contract.spec.Contract
+base class or in a static import. Spring Cloud Contract will execute that method
+in the generated test.

8.11 Multiple Contracts in One File

You can define multiple contracts in one file. Such a contract might resemble the +following example:

Groovy DSL.  +

import org.springframework.cloud.contract.spec.Contract
 
 [
         Contract.make {
@@ -1166,7 +1768,7 @@ following example:

'/users/1')
             }
             response {
-                status 200
+                status OK()
             }
         },
         Contract.make {
@@ -1175,10 +1777,26 @@ following example:

'/users/2')
             }
             response {
-                status 200
+                status OK()
             }
         }
-]

In the preceding example, one contract has the name field and the other does not. This +]

+

YAML.  +

---
+name: should post a user
+request:
+  method: POST
+  url: /users/1
+response:
+  status: 200
+
+---
+request:
+  method: POST
+  url: /users/2
+response:
+  status: 200

+

In the preceding example, one contract has the name field and the other does not. This leads to generation of two tests that look more or less like this:

package org.springframework.cloud.contract.verifier.tests.com.hello;
 
 import com.example.TestBase;
@@ -1226,5 +1844,93 @@ leads to generation of two tests that look more or less like this:

The generated stubs is shown in the following example:

should post a user.json
 1_WithList.json

As you can see, the first file got the name parameter from the contract. The second got the name of the contract file (WithList.groovy) prefixed with the index (in this -case, the contract had an index of 1 in the list of contracts in the file).

[Tip]Tip

As you can see, it iss much better if you name your contracts because doing so makes -your tests far more meaningful.

\ No newline at end of file +case, the contract had an index of 1 in the list of contracts in the file).

[Tip]Tip

As you can see, it is much better if you name your contracts because doing so makes +your tests far more meaningful.

8.12 Generating Spring REST Docs snippets from the contracts

When you want to include the requests and responses of your API using Spring REST Docs, +you only need to make some minor changes to your setup if you are using MockMvc and RestAssuredMockMvc. +Simply include the following dependencies if you haven’t already.

Maven.  +

<dependency>
+	<groupId>org.springframework.cloud</groupId>
+	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
+	<scope>test</scope>
+</dependency>
+<dependency>
+	<groupId>org.springframework.restdocs</groupId>
+	<artifactId>spring-restdocs-mockmvc</artifactId>
+	<optional>true</optional>
+</dependency>

+

Gradle.  +

testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
+testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc'

+

Next you need to make some changes to your base class like the following example.

package com.example.fraud;
+
+import io.restassured.module.mockmvc.RestAssuredMockMvc;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = Application.class)
+public abstract class FraudBaseWithWebAppSetup {
+
+	private static final String OUTPUT = "target/generated-snippets";
+
+	@Rule
+	public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
+
+	@Rule public TestName testName = new TestName();
+
+	@Autowired
+	private WebApplicationContext context;
+
+	@Before
+	public void setup() {
+	RestAssuredMockMvc.mockMvc(MockMvcBuilders.webAppContextSetup(this.context)
+			.apply(documentationConfiguration(this.restDocumentation))
+			.alwaysDo(document(getClass().getSimpleName() + "_" + testName.getMethodName()))
+			.build());
+	}
+
+	protected void assertThatRejectionReasonIsNull(Object rejectionReason) {
+		assert rejectionReason == null;
+	}
+}

In case you are using the standalone setup, you can set up RestAssuredMockMvc like this:

package com.example.fraud;
+
+import io.restassured.module.mockmvc.RestAssuredMockMvc;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TestName;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+
+public abstract class FraudBaseWithStandaloneSetup {
+
+	private static final String OUTPUT = "target/generated-snippets";
+
+	@Rule
+	public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
+
+	@Rule public TestName testName = new TestName();
+
+	@Before
+	public void setup() {
+		RestAssuredMockMvc.standaloneSetup(MockMvcBuilders.standaloneSetup(new FraudDetectionController())
+				.apply(documentationConfiguration(this.restDocumentation))
+				.alwaysDo(document(getClass().getSimpleName() + "_" + testName.getMethodName())));
+	}
+
+}
[Tip]Tip

You don’t need to specify the output directory for the generated snippets since version 1.2.0.RELEASE of Spring REST Docs.

\ No newline at end of file diff --git a/2.0.x/multi/multi__customization.html b/2.0.x/multi/multi__customization.html index f193132145..3522c8d4ec 100644 --- a/2.0.x/multi/multi__customization.html +++ b/2.0.x/multi/multi__customization.html @@ -1,6 +1,6 @@ - 9. Customization

9. Customization

You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in + 9. Customization

9. Customization

[Important]Important

This section is valid only for Groovy DSL

You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in the remainder of this section.

9.1 Extending the DSL

You can provide your own functions to the DSL. The key requirement for this feature is to maintain the static compatibility. Later in this document, you can see examples of:

  • Creating a JAR with reusable classes.
  • Referencing of these classes in the DSLs.

You can find the full example here.

9.1.1 Common JAR

The following examples show three classes that can be reused in the DSLs.

PatternUtils contains functions used by both the consumer and the producer.

package com.example;
diff --git a/2.0.x/multi/multi__links.html b/2.0.x/multi/multi__links.html
index f5821a78a6..b8e41fb657 100644
--- a/2.0.x/multi/multi__links.html
+++ b/2.0.x/multi/multi__links.html
@@ -1,11 +1,6 @@
 
       
-   13. Links

13. Links

The following links may be helpful when working with Spring Cloud Contract Verifier:

  • Spring Cloud Contract Github + 13. Links \ No newline at end of file diff --git a/2.0.x/multi/multi__migrations.html b/2.0.x/multi/multi__migrations.html index 6c7c589f4c..9d777b3c08 100644 --- a/2.0.x/multi/multi__migrations.html +++ b/2.0.x/multi/multi__migrations.html @@ -1,6 +1,7 @@ - 12. Migrations

    12. Migrations

    This section covers migrating from one version of Spring Cloud Contract Verifier to the + 12. Migrations

    12. Migrations

    [Tip]Tip

    For up to date migration guides please visit +the project’s wiki page.

    This section covers migrating from one version of Spring Cloud Contract Verifier to the next version. It covers the following versions upgrade paths:

    12.1 1.0.x → 1.1.x

    This section covers upgrading from version 1.0 to version 1.1.

    12.1.1 New structure of generated stubs

    In 1.1.x we have introduced a change to the structure of generated stubs. If you have been using the @AutoConfigureWireMock notation to use the stubs from the classpath, it no longer works. The following example shows how the @AutoConfigureWireMock notation diff --git a/2.0.x/multi/multi__spring_cloud_contract_faq.html b/2.0.x/multi/multi__spring_cloud_contract_faq.html index 0cde52bfaa..d0f2614d70 100644 --- a/2.0.x/multi/multi__spring_cloud_contract_faq.html +++ b/2.0.x/multi/multi__spring_cloud_contract_faq.html @@ -1,8 +1,8 @@ - 3. Spring Cloud Contract FAQ

    3. Spring Cloud Contract FAQ

    3.1 Why use Spring Cloud Contract Verifier and not X ?

    For the time being Spring Cloud Contract Verifier is a JVM based tool. So it could be your first pick when you’re already creating + 3. Spring Cloud Contract FAQ

    3. Spring Cloud Contract FAQ

    3.1 Why use Spring Cloud Contract Verifier and not X ?

    For the time being Spring Cloud Contract is a JVM based tool. So it could be your first pick when you’re already creating software for the JVM. This project has a lot of really interesting features but especially quite a few of them definitely make -Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:

    • Possibility to do CDC with messaging
    • Clear and easy to use, statically typed DSL
    • Possibility to copy paste your current JSON file to the contract and only edit its elements
    • Automatic generation of tests from the defined Contract
    • Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory
    • Spring Cloud integration - no discovery service is needed for integration tests

    3.2 What is this value(consumer(), producer()) ?

    One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose. +Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:

    • Possibility to do CDC with messaging
    • Clear and easy to use, statically typed DSL
    • Possibility to copy paste your current JSON file to the contract and only edit its elements
    • Automatic generation of tests from the defined Contract
    • Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory
    • Spring Cloud integration - no discovery service is needed for integration tests
    • Spring Cloud Contract integrates with Pact out of the box and provides easy hooks to extend its functionality
    • Via Docker adds support for any language & framework used

    3.2 I don’t want to write a contract in Groovy!

    No problem. You can write a contract in YAML!

    3.3 What is this value(consumer(), producer()) ?

    One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose. What typically makes that difficult are the hard-coded values of request / response elements. For example dates or ids. Imagine the following JSON request

    {
         "time" : "2016-10-10 20:10:15",
    @@ -45,7 +45,7 @@ sides of the communication. You can pass the values:

    Either via the

    or using the $() method

    $(consumer(...), producer(...))
     $(stub(...), test(...))
    -$(client(...), server(...))

    You can read more about this in the Contract DSL section.

    Calling value() or $() tells Spring Cloud Contract that you will be passing a dynamic value. +$(client(...), server(...))

    You can read more about this in the ??? section.

    Calling value() or $() tells Spring Cloud Contract that you will be passing a dynamic value. Inside the consumer() method you pass the value that should be used on the consumer side (in the generated stub). Inside the producer() method you pass the value that should be used on the producer side (in the generated test).

    [Tip]Tip

    If on one side you have passed the regular expression and you haven’t passed the other, then the other side will get auto-generated.

    Most often you will use that method together with the regex helper method. E.g. consumer(regex('[0-9]{10}')).

    To sum it up the contract for the aforementioned scenario would look more or less like this (the regular expression @@ -60,7 +60,7 @@ for time and UUID are simplified and most likely invalid but we want to keep thi ]) } response { - status 200 + status OK() body([ time : value(producer(regex('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]')), id: value([producer(regex('[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}')) @@ -68,21 +68,21 @@ for time and UUID are simplified and most likely invalid but we want to keep thi ]) } }

[Important]Important

Please read the Groovy docs related to JSON to understand how to -properly structure the request / response bodies.

3.3 How to do Stubs versioning?

3.3.1 API Versioning

Let’s try to answer a question what versioning really means. If you’re referring to the API version then there are +properly structure the request / response bodies.

3.4 How to do Stubs versioning?

3.4.1 API Versioning

Let’s try to answer a question what versioning really means. If you’re referring to the API version then there are different approaches.

  • use Hypermedia, links and do not version your API by any means
  • pass versions through headers / urls

I will not try to answer a question which approach is better. Whatever suit your needs and allows you to generate business value should be picked.

Let’s assume that you do version your API. In that case you should provide as many contracts as many versions you support. -You can create a subfolder for every version or append it to th contract name - whatever suits you more.

3.3.2 JAR versioning

If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.

Let’s assume that you’re doing Continuous Delivery / Deployment which means that you’re generating a new version of +You can create a subfolder for every version or append it to th contract name - whatever suits you more.

3.4.2 JAR versioning

If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.

Let’s assume that you’re doing Continuous Delivery / Deployment which means that you’re generating a new version of the jar each time you go through the pipeline and that jar can go to production at any time. For example your jar version looks like this (it got built on the 20.10.2016 at 20:15:21) :

1.0.0.20161020-201521-RELEASE

In that case your generated stub jar will look like this.

1.0.0.20161020-201521-RELEASE-stubs.jar

In this case you should inside your application.yml or @AutoConfigureStubRunner when referencing stubs provide the latest version of the stubs. You can do that by passing the + sign. Example

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

If the versioning however is fixed (e.g. 1.0.4.RELEASE or 2.1.1) then you have to set the concrete value of the jar -version. Example for 2.1.1.

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})

3.3.3 Dev or prod stubs

You can manipulate the classifier to run the tests against current development version of the stubs of other services +version. Example for 2.1.1.

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})

3.4.3 Dev or prod stubs

You can manipulate the classifier to run the tests against current development version of the stubs of other services or the ones that were deployed to production. If you alter your build to deploy the stubs with the prod-stubs classifier - once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.

Example of tests using development version of stubs

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

Example of tests using production version of stubs

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})

You can pass those values also via properties from your deployment pipeline.

3.4 Common repo with contracts

Another way of storing contracts other than having them with the producer is keeping them in a common place. + once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.

Example of tests using development version of stubs

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

Example of tests using production version of stubs

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})

You can pass those values also via properties from your deployment pipeline.

3.5 Common repo with contracts

Another way of storing contracts other than having them with the producer is keeping them in a common place. It can be related to security issues where the consumers can’t clone the producer’s code. Also if you keep contracts in a single place then you, as a producer, will know how many consumers you have and which -consumer will you break with your local changes.

3.4.1 Repo structure

Let’s assume that we have a producer with coordinates com.example:server and 3 consumers: client1, +consumer will you break with your local changes.

3.5.1 Repo structure

Let’s assume that we have a producer with coordinates com.example:server and 3 consumers: client1, client2, client3. Then in the repository with common contracts you would have the following setup -(which you can checkout here:

├── com
+(which you can checkout here):

├── com
 │   └── example
 │       └── server
 │           ├── client1
@@ -115,15 +115,15 @@ one to one to the contents of the repo.

Example of a <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> - <version>1.5.8.RELEASE</version> + <version>2.0.3.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> - <spring-cloud-contract.version>1.2.2.BUILD-SNAPSHOT</spring-cloud-contract.version> - <spring-cloud-dependencies.version>Edgware.BUILD-SNAPSHOT</spring-cloud-dependencies.version> + <spring-cloud-contract.version>2.1.0.BUILD-SNAPSHOT</spring-cloud-contract.version> + <spring-cloud-dependencies.version>Finchley.BUILD-SNAPSHOT</spring-cloud-dependencies.version> <excludeBuildFolders>true</excludeBuildFolders> </properties> @@ -271,15 +271,16 @@ Those poms are necessary for the consumer side to run mvn </excludes> </fileSet> </fileSets> -</assembly>

3.4.2 Workflow

The workflow would look similar to the one presented in the Step by step guide to CDC. The only difference +</assembly>

3.5.2 Workflow

The workflow would look similar to the one presented in the Step by step guide to CDC. The only difference is that the producer doesn’t own the contracts anymore. So the consumer and the producer have to work on - common contracts in a common repository.

3.4.3 Consumer

When the consumer wants to work on the contracts offline, instead of cloning the producer code, the + common contracts in a common repository.

3.5.3 Consumer

When the consumer wants to work on the contracts offline, instead of cloning the producer code, the consumer team clones the common repository, goes to the required producer’s folder (e.g. com/example/server) -and runs mvn clean install -DskipTests to install locally the stubs converted from the contracts.

[Tip]Tip

You need to have Maven installed locally

3.4.4 Producer

As a producer it’s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency +and runs mvn clean install -DskipTests to install locally the stubs converted from the contracts.

[Tip]Tip

You need to have Maven installed locally

3.5.4 Producer

As a producer it’s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency of the JAR containing the contracts:

<plugin>
 	<groupId>org.springframework.cloud</groupId>
 	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
 	<configuration>
+		<contractsMode>REMOTE</contractsMode>
 		<contractsRepositoryUrl>http://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
 		<contractDependency>
 			<groupId>com.example.standalone</groupId>
@@ -290,12 +291,354 @@ of the JAR containing the contracts:

http://link/to/your/nexus/or/artifactory/or/sth. It will be then unpacked in a local temporary folder
 and contracts present under the com/example/server will be picked as the ones used to generate the
 tests and the stubs. Due to this convention the producer team will know which consumer teams will be broken
-when some incompatible changes are done.

The rest of the flow looks the same.

3.5 Can I have multiple base classes for tests?

Yes! Check out the Different base classes for contracts sections -of either Gradle or Maven plugins.

3.6 How can I debug the request/response being sent by the generated tests client?

The generated tests all boil down to RestAssured in some form or fashion which relies on Apache HttpClient. HttpClient has a facility called wire logging which logs the entire request and response to HttpClient. Spring Boot has a logging common application property for doing this sort of thing, just add this to your application properties

logging.level.org.apache.http.wire=DEBUG

3.6.1 How can I debug the mapping/request/response being sent by WireMock?

Starting from version 1.2.0 we turn on WireMock logging to +when some incompatible changes are done.

The rest of the flow looks the same.

3.5.5 How can I define messaging contracts per topic not per producer?

To avoid messaging contracts duplication in the common repo, when few producers writing messages to one topic, +we could create the structure when the rest contracts would be placed in a folder per producer and messaging +contracts in the folder per topic.

For Maven Project

To make it possible to work on the producer side we could do the following things (all via Maven plugins):

  • Add common repo dependency to your classpath:
<dependency>
+   <groupId>com.example</groupId>
+   <artifactId>common-repo</artifactId>
+   <version>${common-repo.version}</version>
+</dependency>
  • Download the JAR with the contracts and unpack the JAR to target:
<plugin>
+   <groupId>org.apache.maven.plugins</groupId>
+   <artifactId>maven-dependency-plugin</artifactId>
+   <version>3.0.0</version>
+   <executions>
+      <execution>
+         <id>unpack-dependencies</id>
+         <phase>process-resources</phase>
+         <goals>
+            <goal>unpack</goal>
+         </goals>
+         <configuration>
+            <artifactItems>
+               <artifactItem>
+                  <groupId>com.example</groupId>
+                  <artifactId>common-repo</artifactId>
+                  <type>jar</type>
+                  <overWrite>false</overWrite>
+                  <outputDirectory>${project.build.directory}/contracts</outputDirectory>
+               </artifactItem>
+            </artifactItems>
+         </configuration>
+      </execution>
+   </executions>
+</plugin>
  • Rip out all the folders we’re not interested in:
<plugin>
+   <groupId>org.apache.maven.plugins</groupId>
+   <artifactId>maven-antrun-plugin</artifactId>
+   <version>1.8</version>
+   <executions>
+      <execution>
+         <phase>process-resources</phase>
+         <goals>
+            <goal>run</goal>
+         </goals>
+         <configuration>
+            <tasks>
+               <delete includeemptydirs="true">
+                  <fileset dir="${project.build.directory}/contracts">
+                     <include name="**/*" />
+                     <!--Producer artifactId-->
+                     <exclude name="**/${project.artifactId}/**" />
+                     <!--List of the supported topics-->
+                     <exclude name="**/${first-topic}/**" />
+                     <exclude name="**/${second-topic}/**" />
+                  </fileset>
+               </delete>
+            </tasks>
+         </configuration>
+      </execution>
+   </executions>
+</plugin>
  • Run the contract plugin by pointing to the contracts to the folder under target:
<plugin>
+   <groupId>org.springframework.cloud</groupId>
+   <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+   <version>${spring-cloud-contract.version}</version>
+   <extensions>true</extensions>
+   <configuration>
+      <packageWithBaseClasses>com.example</packageWithBaseClasses>
+      <baseClassMappings>
+         <baseClassMapping>
+            <contractPackageRegex>.*intoxication.*</contractPackageRegex>
+            <baseClassFQN>com.example.intoxication.BeerIntoxicationBase</baseClassFQN>
+         </baseClassMapping>
+      </baseClassMappings>
+      <contractsDirectory>${project.build.directory}/contracts</contractsDirectory>
+   </configuration>
+</plugin>

For Gradle Project

  • Add a custom configuration for the common-repo dependency:
ext {
+    conractsGroupId = "com.example"
+    contractsArtifactId = "common-repo"
+    contractsVersion = "1.2.3"
+}
+
+configurations {
+    contracts {
+        transitive = false
+    }
+}
  • Add the common-repo dependency to your classpath:
dependencies {
+    contracts "${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"
+    testCompile "${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"
+}
  • Download the dependency to an appropriate folder:
task getContracts(type: Copy) {
+    from configurations.contracts
+    into new File(project.buildDir, "downloadedContracts")
+}
  • Unzip JAR:
task unzipContracts(type: Copy) {
+    def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar")
+    def outputDir = file("${buildDir}/unpackedContracts")
+
+    from zipTree(zipFile)
+    into outputDir
+}
  • Cleanup unused contracts:
task deleteUnwantedContracts(type: Delete) {
+    delete fileTree(dir: "${buildDir}/unpackedContracts",
+        include: "**/*",
+        excludes: [
+            "**/${project.name}/**"",
+            "**/${first-topic}/**",
+            "**/${second-topic}/**"])
+}
  • Create task dependencies:
unzipContracts.dependsOn("getContracts")
+deleteUnwantedContracts.dependsOn("unzipContracts")
+build.dependsOn("deleteUnwantedContracts")
  • Configure plugin by specifying the directory containing contracts using contractsDslDir property
contracts {
+    contractsDslDir = new File("${buildDir}/unpackedContracts")
+}

3.6 Do I need a Binary Storage? Can’t I use Git?

In the polyglot world, there are languages that don’t use binary storages like +Artifactory or Nexus. Starting from Spring Cloud Contract version 2.0.0 we provide +mechanisms to store contracts and stubs in a SCM repository. Currently the +only supported SCM is Git.

The repository would have to the following setup +(which you can checkout here):

.
+└── META-INF
+    └── com.example
+        └── beer-api-producer-git
+            └── 0.0.1-SNAPSHOT
+                ├── contracts
+                │   └── beer-api-consumer
+                │       ├── messaging
+                │       │   ├── shouldSendAcceptedVerification.groovy
+                │       │   └── shouldSendRejectedVerification.groovy
+                │       └── rest
+                │           ├── shouldGrantABeerIfOldEnough.groovy
+                │           └── shouldRejectABeerIfTooYoung.groovy
+                └── mappings
+                    └── beer-api-consumer
+                        └── rest
+                            ├── shouldGrantABeerIfOldEnough.json
+                            └── shouldRejectABeerIfTooYoung.json

Under META-INF folder:

  • we group applications via groupId (e.g. com.example)
  • then each application is represented via the artifactId (e.g. beer-api-producer-git)
  • next, the version of the application. The version is mandatory! (e.g. 0.0.1-SNAPSHOT)
  • finally, there are two folders:

    • contracts - the good practice is to store the contracts required by each +consumer in the folder with the consumer name (e.g. beer-api-consumer). That way you +can use the stubs-per-consumer feature. Further directory structure is arbitrary.
    • mappings - in this folder the Maven / Gradle Spring Cloud Contract plugins will push +the stub server mappings. On the consumer side, Stub Runner will scan this folder +to start stub servers with stub definitions. The folder structure will be a copy +of the one created in the contracts subfolder.

3.6.1 Protocol convention

In order to control the type and location of the source of contracts (whether it’s +a binary storage or an SCM repository), you can use the protocol in the URL of +the repository. Spring Cloud Contract iterates over registered protocol resolvers +and tries to fetch the contracts (via a plugin) or stubs (via Stub Runner).

For the SCM functionality, currently, we support the Git repository. To use it, +in the property, where the repository URL needs to be placed you just have to prefix +the connection URL with git://. Here you can find a couple of examples:

git://file:///foo/bar
+git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
+git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git

3.6.2 Producer

For the producer, to use the SCM approach, we can reuse the +same mechanism we use for external contracts. We route Spring Cloud Contract +to use the SCM implementation via the URL that contains +the git:// protocol.

[Important]Important

You have to manually add the pushStubsToScm +goal in Maven or execute (bind) the pushStubsToScm task in +Gradle. We don’t push stubs to origin of your git +repository out of the box.

Maven.  +

<plugin>
+    <groupId>org.springframework.cloud</groupId>
+    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+    <version>${spring-cloud-contract.version}</version>
+    <extensions>true</extensions>
+    <configuration>
+        <!-- Base class mappings etc. -->
+
+        <!-- We want to pick contracts from a Git repository -->
+        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>
+
+        <!-- We reuse the contract dependency section to set up the path
+        to the folder that contains the contract definitions. In our case the
+        path will be /groupId/artifactId/version/contracts -->
+        <contractDependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <version>${project.version}</version>
+        </contractDependency>
+
+        <!-- The contracts mode can't be classpath -->
+        <contractsMode>REMOTE</contractsMode>
+    </configuration>
+    <executions>
+        <execution>
+            <phase>package</phase>
+            <goals>
+                <!-- By default we will not push the stubs back to SCM,
+                you have to explicitly add it as a goal -->
+                <goal>pushStubsToScm</goal>
+            </goals>
+        </execution>
+    </executions>
+</plugin>

+

Gradle.  +

contracts {
+	// We want to pick contracts from a Git repository
+	contractDependency {
+		stringNotation = "${project.group}:${project.name}:${project.version}"
+	}
+	/*
+	We reuse the contract dependency section to set up the path
+	to the folder that contains the contract definitions. In our case the
+	path will be /groupId/artifactId/version/contracts
+	 */
+	contractRepository {
+		repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
+	}
+	// The mode can't be classpath
+	contractsMode = "REMOTE"
+	// Base class mappings etc.
+}
+
+/*
+In this scenario we want to publish stubs to SCM whenever
+the `publish` task is executed
+*/
+publish.dependsOn("publishStubsToScm")

+

With such a setup:

  • Git project will be cloned to a temporary directory
  • The SCM stub downloader will go to META-INF/groupId/artifactId/version/contracts folder +to find contracts. E.g. for com.example:foo:1.0.0 the path would be +META-INF/com.example/foo/1.0.0/contracts
  • Tests will be generated from the contracts
  • Stubs will be created from the contracts
  • Once the tests pass, the stubs will be committed in the cloned repository
  • Finally, a push will be done to that repo’s origin

3.6.3 Consumer

On the consumer side when passing the repositoryRoot parameter, +either from the @AutoConfigureStubRunner annotation, the +JUnit rule or properties, it’s enough to pass the URL of the +SCM repository, prefixed with the protocol. For example

@AutoConfigureStubRunner(
+    stubsMode="REMOTE",
+    repositoryRoot="git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
+    ids="com.example:bookstore:0.0.1.RELEASE"
+)

With such a setup:

  • Git project will be cloned to a temporary directory
  • The SCM stub downloader will go to META-INF/groupId/artifactId/version/ folder +to find stub definitions and contracts. E.g. for com.example:foo:1.0.0 the path would be +META-INF/com.example/foo/1.0.0/
  • Stub servers will be started and fed with mappings
  • Messaging definitions will be read and used in the messaging tests

3.7 Can I use the Pact Broker?

When using Pact you can use the Pact Broker +to store and share Pact definitions. Starting from Spring Cloud Contract +2.0.0 one can fetch Pact files from the Pact Broker to generate +tests and stubs.

As a prerequisite the Pact Converter and Pact Stub Downloader +are required. You have to add it via the spring-cloud-contract-pact dependency. +You can read more about it in the Section 10.1.1, “Pact Converter” section.

[Important]Important

Pact follows the Consumer Contract convention. That means +that the Consumer creates the Pact definitions first, then +shares the files with the Producer. Those expectations are generated +from the Consumer’s code and can break the Producer if the expectation +is not met.

3.7.1 Pact Consumer

The consumer uses Pact framework to generate Pact files. The +Pact files are sent to the Pact Broker. An example of such +setup can be found here.

3.7.2 Producer

For the producer, to use the Pact files from the Pact Broker, we can reuse the +same mechanism we use for external contracts. We route Spring Cloud Contract +to use the Pact implementation via the URL that contains +the pact:// protocol. It’s enough to pass the URL to the +Pact Broker. An example of such setup can be found here.

Maven.  +

<plugin>
+    <groupId>org.springframework.cloud</groupId>
+    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+    <version>${spring-cloud-contract.version}</version>
+    <extensions>true</extensions>
+    <configuration>
+        <!-- Base class mappings etc. -->
+
+        <!-- We want to pick contracts from a Git repository -->
+        <contractsRepositoryUrl>pact://http://localhost:8085</contractsRepositoryUrl>
+
+        <!-- We reuse the contract dependency section to set up the path
+        to the folder that contains the contract definitions. In our case the
+        path will be /groupId/artifactId/version/contracts -->
+        <contractDependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>${project.artifactId}</artifactId>
+            <!-- When + is passed, a latest tag will be applied when fetching pacts -->
+            <version>+</version>
+        </contractDependency>
+
+        <!-- The contracts mode can't be classpath -->
+        <contractsMode>REMOTE</contractsMode>
+    </configuration>
+    <!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-contract-pact</artifactId>
+            <version>${spring-cloud-contract.version}</version>
+        </dependency>
+    </dependencies>
+</plugin>

+

Gradle.  +

buildscript {
+	repositories {
+		//...
+	}
+
+	dependencies {
+		// ...
+		// Don't forget to add spring-cloud-contract-pact to the classpath!
+		classpath "org.springframework.cloud:spring-cloud-contract-pact:${contractVersion}"
+	}
+}
+
+contracts {
+	// When + is passed, a latest tag will be applied when fetching pacts
+	contractDependency {
+		stringNotation = "${project.group}:${project.name}:+"
+	}
+	contractRepository {
+		repositoryUrl = "pact://http://localhost:8085"
+	}
+	// The mode can't be classpath
+	contractsMode = "REMOTE"
+	// Base class mappings etc.
+}

+

With such a setup:

  • Pact files will be downloaded from the Pact Broker
  • Spring Cloud Contract will convert the Pact files into tests and stubs
  • The JAR with the stubs gets automatically created as usual

3.7.3 Pact Consumer (Producer Contract approach)

In the scenario where you don’t want to do Consumer Contract approach +(for every single consumer define the expectations) but you’d prefer +to do Producer Contracts (the producer provides the contracts and +publishes stubs), it’s enough to use Spring Cloud Contract with +Stub Runner option. An example of such setup can be found here.

First, remember to add Stub Runner and Spring Cloud Contract Pact module +as test dependencies.

Maven.  +

<dependencyManagement>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-dependencies</artifactId>
+            <version>${spring-cloud.version}</version>
+            <type>pom</type>
+            <scope>import</scope>
+        </dependency>
+    </dependencies>
+</dependencyManagement>
+
+<!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
+<dependencies>
+    <!-- ... -->
+    <dependency>
+        <groupId>org.springframework.cloud</groupId>
+        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.springframework.cloud</groupId>
+        <artifactId>spring-cloud-contract-pact</artifactId>
+        <scope>test</scope>
+    </dependency>
+</dependencies>

+

Gradle.  +

dependencyManagement {
+    imports {
+        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
+    }
+}
+
+dependencies {
+    //...
+    testCompile("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
+    // Don't forget to add spring-cloud-contract-pact to the classpath!
+    testCompile("org.springframework.cloud:spring-cloud-contract-pact")
+}

+

Next, just pass the URL of the Pact Broker to repositoryRoot, prefixed +with pact:// protocol. E.g. pact://http://localhost:8085

@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE,
+		ids = "com.example:beer-api-producer-pact",
+		repositoryRoot = "pact://http://localhost:8085")
+public class BeerControllerTest {
+    //Inject the port of the running stub
+    @StubRunnerPort("beer-api-producer-pact") int producerPort;
+    //...
+}

With such a setup:

  • Pact files will be downloaded from the Pact Broker
  • Spring Cloud Contract will convert the Pact files into stub definitions
  • The stub servers will be started and fed with stubs

For more information about Pact support you can go to +the Section 10.7, “Using the Pact Stub Downloader” section.

3.8 How can I debug the request/response being sent by the generated tests client?

The generated tests all boil down to RestAssured in some form or fashion which relies on Apache HttpClient. HttpClient has a facility called wire logging which logs the entire request and response to HttpClient. Spring Boot has a logging common application property for doing this sort of thing, just add this to your application properties

logging.level.org.apache.http.wire=DEBUG

3.8.1 How can I debug the mapping/request/response being sent by WireMock?

Starting from version 1.2.0 we turn on WireMock logging to info and the WireMock notifier to being verbose. Now you will exactly know what request was received by WireMock server and which -matching response definition was picked.

To turn off this feature just bump WireMock logging to ERROR

logging.level.com.github.tomakehurst.wiremock=ERROR

3.6.2 How can I see what got registered in the HTTP server stub?

You can use the mappingsOutputFolder property on @AutoConfigureStubRunner or StubRunnerRule +matching response definition was picked.

To turn off this feature just bump WireMock logging to ERROR

logging.level.com.github.tomakehurst.wiremock=ERROR

3.8.2 How can I see what got registered in the HTTP server stub?

You can use the mappingsOutputFolder property on @AutoConfigureStubRunner or StubRunnerRule to dump all mappings per artifact id. Also the port at which the given stub server was -started will be attached.

3.6.3 Can I reference the request from the response?

Yes! With version 1.1.0 we’ve added such a possibility. On the HTTP stub server side we’re providing support -for this for WireMock. In case of other HTTP server stubs you’ll have to implement the approach yourself.

3.6.4 Can I reference text from file?

Yes! With version 1.2.0 we’ve added such a possibility. It’s enough to call file(…​) method in the -DSL and provide a path relative to where the contract lays.

\ No newline at end of file +started will be attached.

3.8.3 Can I reference text from file?

Yes! With version 1.2.0 we’ve added such a possibility. It’s enough to call file(…​) method in the +DSL and provide a path relative to where the contract lays. +If you’re using YAML just use the bodyFromFile property.

\ No newline at end of file diff --git a/2.0.x/multi/multi__spring_cloud_contract_stub_runner.html b/2.0.x/multi/multi__spring_cloud_contract_stub_runner.html index 5b7065baed..3fb6c249ea 100644 --- a/2.0.x/multi/multi__spring_cloud_contract_stub_runner.html +++ b/2.0.x/multi/multi__spring_cloud_contract_stub_runner.html @@ -67,7 +67,7 @@ versions, which are automatically uploaded after every successful build:

"http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/release" } }

-

6.2 Publishing Stubs as JARs

The easiest approach would be to centralize the way stubs are kept. For example, you can +

6.2 Publishing Stubs as JARs

The easiest approach would be to centralize the way stubs are kept. For example, you can keep them as jars in a Maven repository.

[Tip]Tip

For both Maven and Gradle, the setup comes ready to work. However, you can customize it if you want to.

Maven. 

<!-- First disable the default jar setup in the properties section -->
@@ -89,7 +89,9 @@ it if you want to.

Maven.  <inherited>false</inherited> <configuration> <attach>true</attach> - <descriptor>${basedir}/src/assembly/stub.xml</descriptor> + <descriptors> + ${basedir}/src/assembly/stub.xml + </descriptors> </configuration> </execution> </executions> @@ -157,12 +159,10 @@ publishing { }

6.3 Stub Runner Core

Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of Consumer Driven Contracts.

Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions. -For messaging, special stub routes are defined.

6.3.1 Retrieving stubs

You can pick the following options of acquiring stubs

  • Aether based solution that downloads JARs with stubs from Artifactory / Nexus
  • Classpath scanning solution that searches classpath via pattern to retrieve stubs
  • Write your own implementation of the org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder for full customization

The latter example is described in the Custom Stub Runner section.

Stub downloading

If you provide the stubrunner.repositoryRoot or stubrunner.workOffline flag will be set -to true then Stub Runner will connect to the given server and download the required jars. -It will then unpack the JAR to a temporary folder and reference those files in further -contract processing.

Example:

@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095")

Classpath scanning

If you DON’T provide the stubrunner.repositoryRoot and stubrunner.workOffline flag will -be set to false (that’s the default) then classpath will get scanned. Let’s look at the -following example:

@AutoConfigureStubRunner(ids = {
+For messaging, special stub routes are defined.

6.3.1 Retrieving stubs

You can pick the following options of acquiring stubs

  • Aether based solution that downloads JARs with stubs from Artifactory / Nexus
  • Classpath scanning solution that searches classpath via pattern to retrieve stubs
  • Write your own implementation of the org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder for full customization

The latter example is described in the Custom Stub Runner section.

Stub downloading

You can control the stub downloading via the stubsMode switch. It picks value from the +StubRunnerProperties.StubsMode enum. You can use the following options

  • StubRunnerProperties.StubsMode.CLASSPATH (default value) - will pick stubs from the classpath
  • StubRunnerProperties.StubsMode.LOCAL - will pick stubs from a local storage (e.g. .m2)
  • StubRunnerProperties.StubsMode.REMOTE - will pick stubs from a remote location

Example:

@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)

Classpath scanning

If you set the stubsMode property to StubRunnerProperties.StubsMode.CLASSPATH +(or set nothing since CLASSPATH is the default value) then classpath will get scanned. +Let’s look at the following example:

@AutoConfigureStubRunner(ids = {
     "com.example:beer-api-producer:+:stubs:8095",
     "com.example.foo:bar:1.0.0:superstubs:8096"
 })

If you’ve added the dependencies to your classpath

Maven.  @@ -207,8 +207,8 @@ producer stubs.

The producer would setup the contr    └── com.example       └── beer-api-producer-restdocs       └── nested -       └── contract3.groovy

To achieve proper stub packaging.

Or using the Maven assembly plugin or -Gradle Jar task you have to create the following +       └── contract3.groovy

To achieve proper stub packaging.

Or using the Maven assembly plugin or +Gradle Jar task you have to create the following structure in your stubs jar.

└── META-INF
     └── com.example
         └── beer-api-producer-restdocs
@@ -247,10 +247,10 @@ HTTP stubs without the need to download artifacts.

'false'

HTTP Stubs

Stubs are defined in JSON documents, whose syntax is defined in WireMock documentation

Example:

{
+                                  repository

HTTP Stubs

Stubs are defined in JSON documents, whose syntax is defined in WireMock documentation

Example:

{
     "request": {
         "method": "GET",
         "url": "/ping"
@@ -326,6 +326,7 @@ Check their 
 	Map<StubConfiguration, Collection<Contract>> getContracts();
 }

Example of usage in Spock tests:

@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
+		.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
 		.repoRoot(StubRunnerRuleSpec.getResource("/m2repo/repository").toURI().toString())
 		.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance")
 		.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
@@ -379,7 +380,8 @@ then(rule.findStubUrl(StubFinder interface and use
 its methods as presented below:

@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
 @SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
-		'foo=${stubrunner.runningstubs.fraudDetectionServer.port}'])
+		'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
+		'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
 @AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/")
 @DirtiesContext
 @ActiveProfiles("test")
@@ -387,6 +389,8 @@ its methods as presented below:

@Autowired StubFinder stubFinder
 	@Autowired Environment environment
+	@StubRunnerPort("fraudDetectionServer") int fraudDetectionServerPort
+	@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer") int fraudDetectionServerPortWithGroupId
 	@Value('${foo}') Integer foo
 
 	@BeforeClass
@@ -431,6 +435,9 @@ its methods as presented below:

"stubrunner.runningstubs.fraudDetectionServer.port") != null
 			stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
+		and:
+			environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
+			stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
 	}
 
 	def 'should be able to interpolate a running stub in the passed test property'() {
@@ -439,9 +446,20 @@ its methods as presented below:

0
 			environment.getProperty("foo", Integer) == fraudPort
+			environment.getProperty("fooWithGroup", Integer) == fraudPort
 			foo == fraudPort
 	}
 
+	@Issue("#573")
+	def 'should be able to retrieve the port of a running stub via an annotation'() {
+		given:
+			int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
+		expect:
+			fraudPort > 0
+			fraudDetectionServerPort == fraudPort
+			fraudDetectionServerPortWithGroupId == fraudPort
+	}
+
 	def 'should dump all mappings to a file'() {
 		when:
 			def url = stubFinder.findStubUrl("fraudDetectionServer")
@@ -457,14 +475,21 @@ its methods as presented below:

@AutoConfigureStubRunner.
+    - org.springframework.cloud.contract.verifier.stubs:bootService
+  stubs-mode: remote

Instead of using the properties you can also use the properties inside the @AutoConfigureStubRunner. Below you can find an example of achieving the same result by setting values on the annotation.

@AutoConfigureStubRunner(
 		ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
 		"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
 		"org.springframework.cloud.contract.verifier.stubs:bootService"],
+		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
 		repositoryRoot = "classpath:m2repo/repository/")

Stub Runner Spring registers environment variables in the following manner for every registered WireMock server. Example for Stub Runner ids - com.example:foo, com.example:bar.

  • stubrunner.runningstubs.foo.port
  • stubrunner.runningstubs.bar.port

Which you can reference in your code.

6.5 Stub Runner Spring Cloud

Stub Runner can integrate with Spring Cloud.

For real life examples you can check the

6.5.1 Stubbing Service Discovery

The most important feature of Stub Runner Spring Cloud is the fact that it’s stubbing

  • DiscoveryClient
  • Ribbon ServerList

that means that regardless of the fact whether you’re using Zookeeper, Consul, Eureka or anything else, you don’t need that in your tests. + com.example:foo, com.example:bar.

  • stubrunner.runningstubs.foo.port
  • stubrunner.runningstubs.com.example.foo.port
  • stubrunner.runningstubs.bar.port
  • stubrunner.runningstubs.com.example.bar.port

Which you can reference in your code.

You can also use the @StubRunnerPort annotation to inject the port of a running stub. +Value of the annotation can be the groupid:artifactid or just the artifactid. Example for Stub Runner ids +com.example:foo, com.example:bar.

@StubRunnerPort("foo")
+int fooPort;
+@StubRunnerPort("com.example:bar")
+int barPort;

6.5 Stub Runner Spring Cloud

Stub Runner can integrate with Spring Cloud.

For real life examples you can check the

6.5.1 Stubbing Service Discovery

The most important feature of Stub Runner Spring Cloud is the fact that it’s stubbing

  • DiscoveryClient
  • Ribbon ServerList

that means that regardless of the fact whether you’re using Zookeeper, Consul, Eureka or anything else, you don’t need that in your tests. We’re starting WireMock instances of your dependencies and we’re telling your application whenever you’re using Feign, load balanced RestTemplate or DiscoveryClient directly, to call those stubbed servers instead of calling the real Service Discovery tool.

For example this test will pass

def 'should make service discovery work'() {
 	expect: 'WireMocks are running'
@@ -488,15 +513,17 @@ You can disable Stub Runner Ribbon support by providing: s
 You can disable Stub Runner support by providing: stubrunner.cloud.enabled equal to false

[Tip]Tip

By default all service discovery will be stubbed. That means that regardless of the fact if you have an existing DiscoveryClient its results will be ignored. However, if you want to reuse it, just set stubrunner.cloud.delegate.enabled to true and then your existing DiscoveryClient results will be - merged with the stubbed ones.

6.6 Stub Runner Boot Application

Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to + merged with the stubbed ones.

The default Maven configuration used by Stub Runner can be tweaked either +via the following system properties or environment variables

  • maven.repo.local - path to the custom maven local repository location
  • org.apache.maven.user-settings - path to custom maven user settings location
  • org.apache.maven.global-settings - path to maven global settings location

6.6 Stub Runner Boot Application

Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to trigger the messaging labels and to access started WireMock servers.

One of the use-cases is to run some smoke (end to end) tests on a deployed application. You can check out the Spring Cloud Pipelines -project for more information.

6.6.1 How to use it?

Stub Runner Server

Just add the

compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

Annotate a class with @EnableStubRunnerServer, build a fat-jar and you’re ready to go!

For the properties check the Stub Runner Spring section.

Spring Cloud CLI

Starting from 1.4.0.RELEASE version of the Spring Cloud CLI +project for more information.

6.6.1 How to use it?

Stub Runner Server

Just add the

compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

Annotate a class with @EnableStubRunnerServer, build a fat-jar and you’re ready to go!

For the properties check the Stub Runner Spring section.

Stub Runner Server Fat Jar

You can download a standalone JAR from Maven (for example, for version 1.2.3.RELEASE), as follows:

$ wget -O stub-runner.jar 'https://search.maven.org/remote_content?g=org.springframework.cloud&a=spring-cloud-contract-stub-runner-boot&v=1.2.3.RELEASE'
+$ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...

Spring Cloud CLI

Starting from 1.4.0.RELEASE version of the Spring Cloud CLI project you can start Stub Runner Boot by executing spring cloud stubrunner.

In order to pass the configuration just create a stubrunner.yml file in the current working directory or a subdirectory called config or in ~/.spring-cloud. The file could look like this (example for running stubs installed locally)

stubrunner.yml. 

stubrunner:
-  workOffline: true
+  stubsMode: LOCAL
   ids:
     - com.example:beer-api-producer:+:9876

and then just call spring cloud stubrunner from your terminal window to start @@ -627,7 +654,7 @@ There are 2 consumers: foo-consumer and 200 + status OK() body( foo: "foo" } @@ -636,7 +663,7 @@ response { method GET() } response { - status 200 + status OK() body( bar: "bar" } @@ -656,6 +683,7 @@ Or set the test as follows:

@SpringBootTest(properties = ["spring.application.name=bar-consumer"])
 @AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
 		repositoryRoot = "classpath:m2repo/repository/",
+		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
 		stubsPerConsumer = true)
 @DirtiesContext
 class StubRunnerStubsPerConsumerSpec extends Specification {
@@ -666,6 +694,7 @@ Or set the test as follows:

@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
 		repositoryRoot = "classpath:m2repo/repository/",
 		consumerName = "foo-consumer",
+		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
 		stubsPerConsumer = true)
 @DirtiesContext
 class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
@@ -673,8 +702,7 @@ Or set the test as follows:

foo-consumer in its name (i.e. those from the
 src/test/resources/contracts/foo-consumer/some/contracts/…​ folder) will be allowed to be referenced.

You can check out issue 224 for more information about the reasons behind this change.

6.8 Common

This section briefly describes common properties, including:

6.8.1 Common Properties for JUnit and Spring

You can set repetitive properties by using system properties or Spring configuration -properties. Here are their names with their default values:

Property nameDefault valueDescription

stubrunner.minPort

10000

Minimum value of a port for a started WireMock with stubs.

stubrunner.maxPort

15000

Maximum value of a port for a started WireMock with stubs.

stubrunner.repositoryRoot

 

Maven repo URL. If blank, then call the local maven repo.

stubrunner.classifier

stubs

Default classifier for the stub artifacts.

stubrunner.workOffline

false

If true, then do not contact any remote repositories to -download stubs.

stubrunner.ids

 

Array of Ivy notation stubs to download.

stubrunner.username

 

Optional username to access the tool that stores the JARs with +properties. Here are their names with their default values:

Property nameDefault valueDescription

stubrunner.minPort

10000

Minimum value of a port for a started WireMock with stubs.

stubrunner.maxPort

15000

Maximum value of a port for a started WireMock with stubs.

stubrunner.repositoryRoot

 

Maven repo URL. If blank, then call the local maven repo.

stubrunner.classifier

stubs

Default classifier for the stub artifacts.

stubrunner.stubsMode

CLASSPATH

The way you want to fetch and register the stubs

stubrunner.ids

 

Array of Ivy notation stubs to download.

stubrunner.username

 

Optional username to access the tool that stores the JARs with stubs.

stubrunner.password

 

Optional password to access the tool that stores the JARs with stubs.

stubrunner.stubsPerConsumer

false

Set to true if you want to use different stubs for each consumer instead of registering all stubs for every consumer.

stubrunner.consumerName

 

If you want to use a stub for each consumer and want to @@ -684,4 +712,31 @@ pass an empty classifier this way: groupId:artifactId:vers downloaded.

port means the port of the WireMock server.

[Important]Important

Starting with version 1.0.4, you can provide a range of versions that you would like the Stub Runner to take into consideration. You can read more about the Aether versioning -ranges here.

\ No newline at end of file +ranges here.

6.9 Stub Runner Docker

We’re publishing a spring-cloud/spring-cloud-contract-stub-runner Docker image +that will start the standalone version of Stub Runner.

If you want to learn more about the basics of Maven, artifact ids, +group ids, classifiers and Artifact Managers, just click here Section 4.6, “Docker Project”.

6.9.1 How to use it

Just execute the docker image. You can pass any of the Section 6.8.1, “Common Properties for JUnit and Spring” +as environment variables. The convention is that all the +letters should be upper case. The camel case notation should +and the dot (.) should be separated via underscore (_). E.g. + the stubrunner.repositoryRoot property should be represented + as a STUBRUNNER_REPOSITORY_ROOT environment variable.

6.9.2 Example of client side usage in a non JVM project

We’d like to use the stubs created in this Section 4.6.4, “Server side (nodejs)” step. +Let’s assume that we want to run the stubs on port 9876. The NodeJS code +is available here:

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
+$ cd bookstore

Let’s run the Stub Runner Boot application with the stubs.

# Provide the Spring Cloud Contract Docker version
+$ SC_CONTRACT_DOCKER_VERSION="..."
+# The IP at which the app is running and Docker container can reach it
+$ APP_IP="192.168.0.100"
+# Spring Cloud Contract Stub Runner properties
+$ STUBRUNNER_PORT="8083"
+# Stub coordinates 'groupId:artifactId:version:classifier:port'
+$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
+$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
+# Run the docker with Stub Runner Boot
+$ docker run  --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"

What’s happening is that

  • a standalone Stub Runner application got started
  • it downloaded the stub with coordinates com.example:bookstore:0.0.1.RELEASE:stubs on port 9876
  • it got downloaded from Artifactory running at http://192.168.0.100:8081/artifactory/libs-release-local
  • after a while Stub Runner will be running on port 8083
  • and the stubs will be running at port 9876

On the server side we built a stateful stub. Let’s use curl to assert +that the stubs are setup properly.

# let's execute the first request (no response is returned)
+$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
+# Now time for the second request
+$ curl -X GET http://localhost:9876/api/books
+# You will receive contents of the JSON
[Important]Important

If you want use the stubs that you have built locally, on your host, +then you should pass the environment variable -e STUBRUNNER_STUBS_MODE=LOCAL and mount +the volume of your local m2 -v "${HOME}/.m2/:/root/.m2:ro"

\ No newline at end of file diff --git a/2.0.x/multi/multi__spring_cloud_contract_verifier_introduction.html b/2.0.x/multi/multi__spring_cloud_contract_verifier_introduction.html index 6c9b105507..1e3d3e4900 100644 --- a/2.0.x/multi/multi__spring_cloud_contract_verifier_introduction.html +++ b/2.0.x/multi/multi__spring_cloud_contract_verifier_introduction.html @@ -9,14 +9,14 @@ produced by Spring Cloud Contract Verifier.
  • Messaging r Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your own integrations.
  • Acceptance tests (in JUnit or Spock) are used to verify if server-side implementation of the API is compliant with the contract (server tests). A full test is generated by -Spring Cloud Contract Verifier.
  • 2.1 Why a Contract Verifier?

    Assume that we have a system consisting of multiple microservices:

    Microservices Architecture

    2.1.1 Testing issues

    If we wanted to test the application in top left corner to determine whether it can +Spring Cloud Contract Verifier.

    2.1 Why a Contract Verifier?

    Assume that we have a system consisting of multiple microservices:

    Microservices Architecture

    2.1.1 Testing issues

    If we wanted to test the application in top left corner to determine whether it can communicate with other services, we could do one of two things:

    • Deploy all microservices and perform end-to-end tests.
    • Mock other microservices in unit/integration tests.

    Both have their advantages but also a lot of disadvantages.

    Deploy all microservices and perform end to end tests

    Advantages:

    • Simulates production.
    • Tests real communication between services.

    Disadvantages:

    • To test one microservice, we have to deploy 6 microservices, a couple of databases, etc.
    • The environment where the tests run is locked for a single suite of tests (nobody else would be able to run the tests in the meantime).
    • They take a long time to run.
    • The feedback comes very late in the process.
    • They are extremely hard to debug.

    Mock other microservices in unit/integration tests

    Advantages:

    • They provide very fast feedback.
    • They have no infrastructure requirements.

    Disadvantages:

    • The implementor of the service creates stubs that might have nothing to do with reality.
    • You can go to production with passing tests and failing production.

    To solve the aforementioned issues, Spring Cloud Contract Verifier with Stub Runner was created. The main idea is to give you very fast feedback, without the need to set up the whole world of microservices. If you work on stubs, then the only applications you need -are those that your application directly uses.

    Stubbed Services

    Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were +are those that your application directly uses.

    Stubbed Services

    Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were created by the service that you’re calling. Also, if you can use them, it means that they were tested against the producer’s side. In short, you can trust those stubs.

    2.2 Purposes

    The main purposes of Spring Cloud Contract Verifier with Stub Runner are:

    • To ensure that WireMock/Messaging stubs (used when developing the client) do exactly what the actual server-side implementation does.
    • To promote ATDD method and Microservices architectural style.
    • To provide a way to publish changes in contracts that are immediately visible on both @@ -24,10 +24,227 @@ sides.
    • To generate boilerplate test code to be used on features in the contracts. Assume that we have a business use case of fraud check. If a user can be a fraud for 100 different reasons, we would assume that you would create 2 contracts, one for the positive case and one for the negative case. Contract tests are -used to test contracts between applications and not to simulate full behavior.

    2.3 How It Works

    This section explores how Spring Cloud Contract Verifier with Stub Runner works.

    2.3.1 Defining the contract

    As consumers of services, we need to define what exactly we want to achieve. We need to +used to test contracts between applications and not to simulate full behavior.

    2.3 How It Works

    This section explores how Spring Cloud Contract Verifier with Stub Runner works.

    2.3.1 A Three-second Tour

    This very brief tour walks through using Spring Cloud Contract:

    You can find a somewhat longer tour +here.

    On the Producer Side

    To start working with Spring Cloud Contract, add files with REST/ messaging contracts +expressed in either Groovy DSL or YAML to the contracts directory, which is set by the +contractsDslDir property. By default, it is $rootDir/src/test/resources/contracts.

    Then add the Spring Cloud Contract Verifier dependency and plugin to your build file, as +shown in the following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
    +	<scope>test</scope>
    +</dependency>

    The following listing shows how to add the plugin, which should go in the build/plugins +portion of the file:

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +</plugin>

    Running ./mvnw clean install automatically generates tests that verify the application +compliance with the added contracts. By default, the tests get generated under +org.springframework.cloud.contract.verifier.tests..

    As the implementation of the functionalities described by the contracts is not yet +present, the tests fail.

    To make them pass, you must add the correct implementation of either handling HTTP +requests or messages. Also, you must add a correct base test class for auto-generated +tests to the project. This class is extended by all the auto-generated tests, and it +should contain all the setup necessary to run them (for example RestAssuredMockMvc +controller setup or messaging test setup).

    Once the implementation and the test base class are in place, the tests pass, and both the +application and the stub artifacts are built and installed in the local Maven repository. +The changes can now be merged, and both the application and the stub artifacts may be +published in an online repository.

    On the Consumer Side

    Spring Cloud Contract Stub Runner can be used in the integration tests to get a running +WireMock instance or messaging route that simulates the actual service.

    To do so, add the dependency to Spring Cloud Contract Stub Runner, as shown in the +following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    +	<scope>test</scope>
    +</dependency>

    You can get the Producer-side stubs installed in your Maven repository in either of two +ways:

    • By checking out the Producer side repository and adding contracts and generating the stubs +by running the following commands:

      $ cd local-http-server-repo
      +$ ./mvnw clean install -DskipTests
      [Tip]Tip

      The tests are being skipped because the Producer-side contract implementation is not +in place yet, so the automatically-generated contract tests fail.

    • By getting already-existing producer service stubs from a remote repository. To do so, +pass the stub artifact IDs and artifact repository URL as Spring Cloud Contract +Stub Runner properties, as shown in the following example:

      stubrunner:
      +  ids: 'com.example:http-server-dsl:+:stubs:8080'
      +  repositoryRoot: http://repo.spring.io/libs-snapshot

    Now you can annotate your test class with @AutoConfigureStubRunner. In the annotation, +provide the group-id and artifact-id values for Spring Cloud Contract Stub Runner to +run the collaborators' stubs for you, as shown in the following example:

    @RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment=WebEnvironment.NONE)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    +@DirtiesContext
    +public class LoanApplicationServiceTests {
    [Tip]Tip

    Use the REMOTE stubsMode when downloading stubs from an online repository and +LOCAL for offline work.

    Now, in your integration test, you can receive stubbed versions of HTTP responses or +messages that are expected to be emitted by the collaborator service.

    2.3.2 A Three-minute Tour

    This brief tour walks through using Spring Cloud Contract:

    You can find an even more brief tour +here.

    On the Producer Side

    To start working with Spring Cloud Contract, add files with REST/ messaging contracts +expressed in either Groovy DSL or YAML to the contracts directory, which is set by the +contractsDslDir property. By default, it is $rootDir/src/test/resources/contracts.

    For the HTTP stubs, a contract defines what kind of response should be returned for a +given request (taking into account the HTTP methods, URLs, headers, status codes, and so +on). The following example shows how an HTTP stub contract in Groovy DSL:

    package contracts
    +
    +org.springframework.cloud.contract.spec.Contract.make {
    +	request {
    +		method 'PUT'
    +		url '/fraudcheck'
    +		body([
    +			   "client.id": $(regex('[0-9]{10}')),
    +			   loanAmount: 99999
    +		])
    +		headers {
    +			contentType('application/json')
    +		}
    +	}
    +	response {
    +		status OK()
    +		body([
    +			   fraudCheckStatus: "FRAUD",
    +			   "rejection.reason": "Amount too high"
    +		])
    +		headers {
    +			contentType('application/json')
    +		}
    +	}
    +}

    The same contract expressed in YAML would look like the following example:

    request:
    +  method: PUT
    +  url: /fraudcheck
    +  body:
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers:
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id']
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response:
    +  status: 200
    +  body:
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers:
    +    Content-Type: application/json;charset=UTF-8

    In the case of messaging, you can define:

    • The input and the output messages can be defined (taking into account from and where it +was sent, the message body, and the header).
    • The methods that should be called after the message is received.
    • The methods that, when called, should trigger a message.

    The following example shows a Camel messaging contract expressed in Groovy DSL:

    def contractDsl = Contract.make {
    +	label 'some_label'
    +	input {
    +		messageFrom('jms:delete')
    +		messageBody([
    +				bookName: 'foo'
    +		])
    +		messageHeaders {
    +			header('sample', 'header')
    +		}
    +		assertThat('bookWasDeleted()')
    +	}
    +}

    The following example shows the same contract expressed in YAML:

    label: some_label
    +input:
    +  messageFrom: jms:delete
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +  assertThat: bookWasDeleted()

    Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file, +as shown in the following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
    +	<scope>test</scope>
    +</dependency>

    The following listing shows how to add the plugin, which should go in the build/plugins +portion of the file:

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +</plugin>

    Running ./mvnw clean install automatically generates tests that verify the application +compliance with the added contracts. By default, the generated tests are under +org.springframework.cloud.contract.verifier.tests..

    The following example shows a sample auto-generated test for an HTTP contract:

    @Test
    +public void validate_shouldMarkClientAsFraud() throws Exception {
    +    // given:
    +        MockMvcRequestSpecification request = given()
    +                .header("Content-Type", "application/vnd.fraud.v1+json")
    +                .body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
    +
    +    // when:
    +        ResponseOptions response = given().spec(request)
    +                .put("/fraudcheck");
    +
    +    // then:
    +        assertThat(response.statusCode()).isEqualTo(200);
    +        assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
    +    // and:
    +        DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
    +        assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
    +        assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
    +}

    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 +used. (To do so, change the testMode property of the plugin to JAX-RS or EXPLICIT, +respectively.)

    Apart from the default JUnit, you can instead use Spock tests, by setting the plugin +testFramework property to Spock.

    [Tip]Tip

    You can now also generate WireMock scenarios based on the contracts, by including an +order number followed by an underscore at the beginning of the contract file names.

    The following example shows an auto-generated test in Spock for a messaging stub contract:

    [source,groovy,indent=0]
    given:
    +	 ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
    +		\'\'\'{"bookName":"foo"}\'\'\',
    +		['sample': 'header']
    +	)
    +
    +when:
    +	 contractVerifierMessaging.send(inputMessage, 'jms:delete')
    +
    +then:
    +	 noExceptionThrown()
    +	 bookWasDeleted()

    As the implementation of the functionalities described by the contracts is not yet +present, the tests fail.

    To make them pass, you must add the correct implementation of handling either HTTP +requests or messages. Also, you must add a correct base test class for auto-generated +tests to the project. This class is extended by all the auto-generated tests and should +contain all the setup necessary to run them (for example, RestAssuredMockMvc controller +setup or messaging test setup).

    Once the implementation and the test base class are in place, the tests pass, and both the +application and the stub artifacts are built and installed in the local Maven repository. +Information about installing the stubs jar to the local repository appears in the logs, as +shown in the following example:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
    +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
    +[INFO]
    +[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
    +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
    +[INFO]
    +[INFO] --- spring-boot-maven-plugin:1.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
    +[INFO]
    +[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
    +[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar
    +[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom
    +[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

    You can now merge the changes and publish both the application and the stub artifacts +in an online repository.

    Docker Project

    In order to enable working with contracts while creating applications in non-JVM +technologies, the springcloud/spring-cloud-contract Docker image has been created. It +contains a project that automatically generates tests for HTTP contracts and executes them +in EXPLICIT test mode. Then, if the tests pass, it generates Wiremock stubs and, +optionally, publishes them to an artifact manager. In order to use the image, you can +mount the contracts into the /contracts directory and set a few environment variables.

    On the Consumer Side

    Spring Cloud Contract Stub Runner can be used in the integration tests to get a running +WireMock instance or messaging route that simulates the actual service.

    To get started, add the dependency to Spring Cloud Contract Stub Runner:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    +	<scope>test</scope>
    +</dependency>

    You can get the Producer-side stubs installed in your Maven repository in either of two +ways:

    • By checking out the Producer side repository and adding contracts and generating the +stubs by running the following commands:

      $ cd local-http-server-repo
      +$ ./mvnw clean install -DskipTests
      [Note]Note

      The tests are skipped because the Producer-side contract implementation is not yet +in place, so the automatically-generated contract tests fail.

    • Getting already existing producer service stubs from a remote repository. To do so, +pass the stub artifact IDs and artifact repository URl as Spring Cloud Contract Stub +Runner properties, as shown in the following example:

      stubrunner:
      +  ids: 'com.example:http-server-dsl:+:stubs:8080'
      +  repositoryRoot: http://repo.spring.io/libs-snapshot

    Now you can annotate your test class with @AutoConfigureStubRunner. In the annotation, +provide the group-id and artifact-id for Spring Cloud Contract Stub Runner to run +the collaborators' stubs for you, as shown in the following example:

    @RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment=WebEnvironment.NONE)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    +@DirtiesContext
    +public class LoanApplicationServiceTests {
    [Tip]Tip

    Use the REMOTE stubsMode when downloading stubs from an online repository and +LOCAL for offline work.

    In your integration test, you can receive stubbed versions of HTTP responses or messages +that are expected to be emitted by the collaborator service. You can see entries similar +to the following in the build logs:

    2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
    +2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
    +2016-07-19 14:22:25.439  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
    +2016-07-19 14:22:25.451  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
    +2016-07-19 14:22:25.465  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
    +2016-07-19 14:22:25.475  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
    +2016-07-19 14:22:27.737  INFO 41050 --- [           main] o.s.c.c.stubrunner.StubRunnerExecutor    : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]

    2.3.3 Defining the Contract

    As consumers of services, we need to define what exactly we want to achieve. We need to formulate our expectations. That is why we write contracts.

    Assume that you want to send a request containing the ID of a client company and the amount it wants to borrow from us. You also want to send it to the /fraudcheck url via -the PUT method.

    package contracts
    +the PUT method.

    Groovy DSL.  +

    package contracts
     
     org.springframework.cloud.contract.spec.Contract.make {
     	request { // (1)
    @@ -42,7 +259,7 @@ org.springframework.cloud.contract.spec.Contract.make {
     		}
     	}
     	response { // (6)
    -		status 200 // (7)
    +		status OK() // (7)
     		body([ // (8)
     			   fraudCheckStatus: "FRAUD",
     			   "rejection.reason": "Amount too high"
    @@ -60,7 +277,7 @@ From the Consumer perspective, when shooting a request in the integration test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the response will be sent with
    @@ -75,7 +292,7 @@ From the Producer perspective, in the autogenerated producer-side test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the test will assert if the response has been sent with
    @@ -83,21 +300,76 @@ From the Producer perspective, in the autogenerated producer-side test:
     (8) - and JSON body equal to
      { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
     (9) - with header `Content-Type` matching `application/json.*`
    - */

    2.3.2 Client Side

    Spring Cloud Contract generates stubs, which you can use during client-side testing. + */

    +

    YAML.  +

    request: # (1)
    +  method: PUT # (2)
    +  url: /fraudcheck # (3)
    +  body: # (4)
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers: # (5)
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id'] # (6)
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response: # (7)
    +  status: 200 # (8)
    +  body:  # (9)
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers: # (10)
    +    Content-Type: application/json;charset=UTF-8
    +
    +
    +#From the Consumer perspective, when shooting a request in the integration test:
    +#
    +#(1) - If the consumer sends a request
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
    +#(7) - then the response will be sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json`
    +#
    +#From the Producer perspective, in the autogenerated producer-side test:
    +#
    +#(1) - A request will be sent to the producer
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id` `1234567890`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(7) - then the test will assert if the response has been sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

    +

    2.3.4 Client Side

    Spring Cloud Contract generates stubs, which you can use during client-side testing. You get a running WireMock instance/Messaging route that simulates the service. You would like to feed that instance with a proper stub definition.

    At some point in time, you need to send a request to the Fraud Detection service.

    ResponseEntity<FraudServiceResponse> response =
     		restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
     				new HttpEntity<>(request, httpHeaders),
     				FraudServiceResponse.class);

    Annotate your test class with @AutoConfigureStubRunner. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment=WebEnvironment.NONE)
    -@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
     @DirtiesContext
     public class LoanApplicationServiceTests {

    After that, during the tests, Spring Cloud Contract automatically finds the stubs (simulating the real service) in the Maven repository and exposes them on a configured -(or random) port.

    2.3.3 Server Side

    Since you are developing your stub, you need to be sure that it actually resembles your +(or random) port.

    2.3.5 Server Side

    Since you are developing your stub, you need to be sure that it actually resembles your concrete implementation. You cannot have a situation where your stub acts in one way and your application behaves in a different way, especially in production.

    To ensure that your application behaves the way you define in your stub, tests are -generated from the stub you provide.

    The autogenerated test looks like this:

    @Test
    +generated from the stub you provide.

    The autogenerated test looks, more or less, like this:

    @Test
     public void validate_shouldMarkClientAsFraud() throws Exception {
         // given:
             MockMvcRequestSpecification request = given()
    @@ -121,8 +393,8 @@ us. The current implementation of our system grants loans to everybody.

    As sprint, we must develop a new feature: if a client wants to borrow too much money, then we mark the client as a fraud.

    Technical remark - Fraud Detection has an artifact-id of http-server, while Loan Issuance has an artifact-id of http-client, and both have a group-id of com.example.

    Social remark - both client and server development teams need to communicate directly and -discuss changes while going through the process. CDC is all about communication.

    The server -side code is available here and the +discuss changes while going through the process. CDC is all about communication.

    The server +side code is available here and the client code here.

    [Tip]Tip

    In this case, the producer owns the contracts. Physically, all the contract are in the producer’s repository.

    2.4.1 Technical note

    If using the SNAPSHOT / Milestone / Release Candidate versions please add the following section to your build:

    Maven.  @@ -206,9 +478,10 @@ client wants to borrow. You want to send it to the /fraudc FraudServiceResponse.class);

    For simplicity, the port of the Fraud Detection service is set to 8080, and the application runs on 8090.

    If you start the test at this point, it breaks, because no service currently runs on port 8080.

    Clone the Fraud Detection service repository locally.

    You can start by playing around with the server side contract. To do so, you must first -clone it.

    git clone https://your-git-server.com/server-side.git local-http-server-repo

    Define the contract locally in the repo of Fraud Detection service.

    As a consumer, you need to define what exactly you want to achieve. You need to formulate +clone it.

    $ git clone https://your-git-server.com/server-side.git local-http-server-repo

    Define the contract locally in the repo of Fraud Detection service.

    As a consumer, you need to define what exactly you want to achieve. You need to formulate your expectations. To do so, write the following contract:

    [Important]Important

    Place the contract under src/test/resources/contracts/fraud folder. The fraud folder -is important because the producer’s test base class name references that folder.

    package contracts
    +is important because the producer’s test base class name references that folder.

    Groovy DSL.  +

    package contracts
     
     org.springframework.cloud.contract.spec.Contract.make {
     	request { // (1)
    @@ -223,7 +496,7 @@ org.springframework.cloud.contract.spec.Contract.make {
     		}
     	}
     	response { // (6)
    -		status 200 // (7)
    +		status OK() // (7)
     		body([ // (8)
     			   fraudCheckStatus: "FRAUD",
     			   "rejection.reason": "Amount too high"
    @@ -241,7 +514,7 @@ From the Consumer perspective, when shooting a request in the integration test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the response will be sent with
    @@ -256,7 +529,7 @@ From the Producer perspective, in the autogenerated producer-side test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the test will assert if the response has been sent with
    @@ -264,15 +537,69 @@ From the Producer perspective, in the autogenerated producer-side test:
     (8) - and JSON body equal to
      { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
     (9) - with header `Content-Type` matching `application/json.*`
    - */

    The Contract is written using a statically typed Groovy DSL. You might wonder what about -those value(client(…​), server(…​)) parts. By using this notation, Spring Cloud + */

    +

    YAML.  +

    request: # (1)
    +  method: PUT # (2)
    +  url: /fraudcheck # (3)
    +  body: # (4)
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers: # (5)
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id'] # (6)
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response: # (7)
    +  status: 200 # (8)
    +  body:  # (9)
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers: # (10)
    +    Content-Type: application/json;charset=UTF-8
    +
    +
    +#From the Consumer perspective, when shooting a request in the integration test:
    +#
    +#(1) - If the consumer sends a request
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
    +#(7) - then the response will be sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json`
    +#
    +#From the Producer perspective, in the autogenerated producer-side test:
    +#
    +#(1) - A request will be sent to the producer
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id` `1234567890`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(7) - then the test will assert if the response has been sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

    +

    The YML contract is quite straight-forward. However when you take a look at the Contract +written using a statically typed Groovy DSL - you might wonder what the +value(client(…​), server(…​)) parts are. By using this notation, Spring Cloud Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case of an identifier or a timestamp, you need not hardcode a value. You want to allow some different ranges of values. To enable ranges of values, you can set regular expressions matching those values for the consumer side. You can provide the body by means of either a map notation or String with interpolations. -Consult the docs -for more information. We highly recommend using the map notation!

    [Tip]Tip

    You must understand the map notation in order to set up contracts. Please read the +Consult the ??? section for more information. We highly recommend using the map notation!

    [Tip]Tip

    You must understand the map notation in order to set up contracts. Please read the Groovy docs regarding JSON.

    The previously shown contract is an agreement between two sides that:

    • if an HTTP request is sent with all of

      • a PUT method on the /fraudcheck endpoint,
      • a JSON body with a client.id that matches the regular expression [0-9]{10} and loanAmount equal to 99999,
      • and a Content-Type header with a value of application/vnd.fraud.v1+json,
    • then an HTTP response is sent to the consumer that

      • has status 200,
      • contains a JSON body with the fraudCheckStatus field containing a value FRAUD and the rejectionReason field having value Amount too high,
      • and a Content-Type header with a value of application/vnd.fraud.v1+json.

    Once you are ready to check the API in practice in the integration tests, you need to @@ -297,8 +624,8 @@ First, add the Spring Cloud Contract BOM.

    </configuration>
     </plugin>

    Since the plugin was added, you get the Spring Cloud Contract Verifier features which, from the provided contracts:

    • generate and run tests
    • produce and install stubs

    You do not want to generate tests since you, as the consumer, want only to play with the -stubs. You need to skip the test generation and execution. When you execute:

    cd local-http-server-repo
    -./mvnw clean install -DskipTests

    In the logs, you see something like this:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
    +stubs. You need to skip the test generation and execution. When you execute:

    $ cd local-http-server-repo
    +$ ./mvnw clean install -DskipTests

    In the logs, you see something like this:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
     [INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
     [INFO]
     [INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
    @@ -329,9 +656,10 @@ Application service):

    Add the Spring Cloud Co </dependency>

    Annotate your test class with @AutoConfigureStubRunner. In the annotation, provide the group-id and artifact-id for the Stub Runner to download the stubs of your collaborators. (Optional step) Because you’re playing with the collaborators offline, you -can also provide the offline work switch.

    @RunWith(SpringRunner.class)
    +can also provide the offline work switch (StubRunnerProperties.StubsMode.LOCAL).

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment=WebEnvironment.NONE)
    -@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
     @DirtiesContext
     public class LoanApplicationServiceTests {

    Now, when you run your tests, you see something like this:

    2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
     2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
    @@ -347,8 +675,8 @@ you wish.

    Once you are satisfied with the results and the test passes, pub the server side. Currently, the consumer side work is done.

    2.4.3 Producer side (Fraud Detection server)

    As a developer of the Fraud Detection server (a server to the Loan Issuance service):

    Create an initial implementation.

    As a reminder, you can see the initial implementation here:

    @RequestMapping(value = "/fraudcheck", method = PUT)
     public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
     return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
    -}

    Take over the pull request.

    git checkout -b contract-change-pr master
    -git pull https://your-git-server.com/server-side-fork.git contract-change-pr

    You must add the dependencies needed by the autogenerated tests:

    <dependency>
    +}

    Take over the pull request.

    $ git checkout -b contract-change-pr master
    +$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr

    You must add the dependencies needed by the autogenerated tests:

    <dependency>
     	<groupId>org.springframework.cloud</groupId>
     	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
     	<scope>test</scope>
    @@ -418,8 +746,9 @@ like this:

    "['fraudCheckStatus']").matches("[A-Z]{5}");
             assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
    -}

    As you can see, all the producer() parts of the Contract that were present in the -value(consumer(…​), producer(…​)) blocks got injected into the test.

    Note that, on the producer side, you are also doing TDD. The expectations are expressed +}

    If you used the Groovy DSL, you can see, all the producer() parts of the Contract that were present in the +value(consumer(…​), producer(…​)) blocks got injected into the test. +In case of using YAML, the same applied for the matchers sections of the response.

    Note that, on the producer side, you are also doing TDD. The expectations are expressed in the form of a test. This test sends a request to our own application with the URL, headers, and body defined in the contract. It also is expecting precisely defined values in the response. In other words, you have the red part of red, green, and @@ -432,14 +761,14 @@ implementation:

    return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
     }

    When you execute ./mvnw clean install again, the tests pass. Since the Spring Cloud Contract Verifier plugin adds the tests to the generated-test-sources, you can -actually run those tests from your IDE.

    Deploy your app.

    Once you finish your work, you can deploy your change. First, merge the branch:

    git checkout master
    -git merge --no-ff contract-change-pr
    -git push origin master

    Your CI might run something like ./mvnw clean deploy, which would publish both the -application and the stub artifacts.

    2.4.4 Consumer Side (Loan Issuance) Final Step

    As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):

    Merge branch to master.

    git checkout master
    -git merge --no-ff contract-change-pr

    Work online.

    Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate +actually run those tests from your IDE.

    Deploy your app.

    Once you finish your work, you can deploy your change. First, merge the branch:

    $ git checkout master
    +$ git merge --no-ff contract-change-pr
    +$ git push origin master

    Your CI might run something like ./mvnw clean deploy, which would publish both the +application and the stub artifacts.

    2.4.4 Consumer Side (Loan Issuance) Final Step

    As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):

    Merge branch to master.

    $ git checkout master
    +$ git merge --no-ff contract-change-pr

    Work online.

    Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate where the repository with your stubs is located. At this moment the stubs of the server -side are automatically downloaded from Nexus/Artifactory. You can switch off the value of -the workOffline parameter in your annotation. The following code shows an example of +side are automatically downloaded from Nexus/Artifactory. You can set the value of +stubsMode to REMOTE. The following code shows an example of achieving the same thing by changing the properties.

    stubrunner:
       ids: 'com.example:http-server-dsl:+:stubs:8080'
       repositoryRoot: http://repo.spring.io/libs-snapshot

    That’s it!

    2.5 Dependencies

    The best way to add dependencies is to use the proper starter dependency.

    For stub-runner, use spring-cloud-starter-stub-runner. When you use a plugin, add diff --git a/2.0.x/multi/multi__spring_cloud_contract_verifier_messaging.html b/2.0.x/multi/multi__spring_cloud_contract_verifier_messaging.html index 0ddf64c23f..e7c6c48768 100644 --- a/2.0.x/multi/multi__spring_cloud_contract_verifier_messaging.html +++ b/2.0.x/multi/multi__spring_cloud_contract_verifier_messaging.html @@ -1,6 +1,6 @@ - 5. Spring Cloud Contract Verifier Messaging

    5. Spring Cloud Contract Verifier Messaging

    Spring Cloud Contract Verifier lets you verify applications that uses messaging as a + 5. Spring Cloud Contract Verifier Messaging

    5. Spring Cloud Contract Verifier Messaging

    Spring Cloud Contract Verifier lets you verify applications that use messaging as a means of communication. All of the integrations shown in this document work with Spring, but you can also create one of your own and use that.

    5.1 Integrations

    You can use one of the following four integration configurations:

    • Apache Camel
    • Spring Integration
    • Spring Cloud Stream
    • Spring AMQP

    Since we use Spring Boot, if you have added one of these libraries to the classpath, all the messaging configuration is automatically set up.

    [Important]Important

    Remember to put @AutoConfigureMessageVerifier on the base class of your @@ -35,7 +35,8 @@ message is triggered by a component inside the application (for example, schedu meanings for different messaging implementations. For Stream and Integration it is first resolved as a destination of a channel. Then, if there is no such destination it is resolved as a channel name. For Camel, that’s a certain component (for example, -jms).

    5.3.1 Scenario 1: No Input Message

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +jms).

    5.3.1 Scenario 1: No Input Message

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		triggeredBy('bookReturnedTriggered()')
    @@ -48,7 +49,19 @@ it is resolved as a channel name. For Camel

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  triggeredBy: bookReturnedTriggered
    +outputMessage:
    +  sentTo: activemq:output
    +  body:
    +    bookName: foo
    +  headers:
    +    BOOK-NAME: foo
    +    contentType: application/json

    +

    The following JUnit test is created:

    '''
      // when:
       bookReturnedTriggered();
     
    @@ -75,7 +88,8 @@ it is resolved as a channel name. For Camel"bookName").isEqualTo("foo")
     
    -'''

    5.3.2 Scenario 2: Output Triggered by Input

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +'''

    5.3.2 Scenario 2: Output Triggered by Input

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		messageFrom('jms:input')
    @@ -95,7 +109,22 @@ it is resolved as a channel name. For Camel'BOOK-NAME', 'foo')
     		}
     	}
    -}

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  messageFrom: jms:input
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +outputMessage:
    +  sentTo: jms:output
    +  body:
    +    bookName: foo
    +  headers:
    +    BOOK-NAME: foo

    +

    The following JUnit test is created:

    '''
     // given:
      ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
       "{\\"bookName\\":\\"foo\\"}"
    @@ -130,7 +159,8 @@ then:
     and:
        DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
        assertThatJson(parsedJson).field("bookName").isEqualTo("foo")
    -"""

    5.3.3 Scenario 3: No Output Message

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +"""

    5.3.3 Scenario 3: No Output Message

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		messageFrom('jms:delete')
    @@ -142,7 +172,17 @@ and:
     		}
     		assertThat('bookWasDeleted()')
     	}
    -}

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  messageFrom: jms:delete
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +  assertThat: bookWasDeleted()

    +

    The following JUnit test is created:

    '''
     // given:
      ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
     	"{\\"bookName\\":\\"foo\\"}"
    @@ -168,9 +208,7 @@ then:
     	 noExceptionThrown()
     	 bookWasDeleted()
     '''

    5.4 Consumer Stub Generation

    Unlike the HTTP part, in messaging, we need to publish the Groovy DSL inside the JAR with -a stub. Then it is parsed on the consumer side and proper stubbed routes are created.

    For more information, see -the -Stub Runner Messaging sections.

    Maven.  +a stub. Then it is parsed on the consumer side and proper stubbed routes are created.

    For more information, see ??? section.

    Maven. 

    <dependencies>
     	<dependency>
     		<groupId>org.springframework.cloud</groupId>
    diff --git a/2.0.x/multi/multi__spring_cloud_contract_verifier_setup.html b/2.0.x/multi/multi__spring_cloud_contract_verifier_setup.html
    index 36e6d97b1d..fa7f7fa8c8 100644
    --- a/2.0.x/multi/multi__spring_cloud_contract_verifier_setup.html
    +++ b/2.0.x/multi/multi__spring_cloud_contract_verifier_setup.html
    @@ -1,7 +1,7 @@
     
           
    -   4. Spring Cloud Contract Verifier Setup

    4. Spring Cloud Contract Verifier Setup

    You can set up Spring Cloud Contract Verifier in either of two ways

    4.1 Gradle Project

    To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the -following sections:

    4.1.1 Prerequisites

    In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a + 4. Spring Cloud Contract Verifier Setup

    4. Spring Cloud Contract Verifier Setup

    You can set up Spring Cloud Contract Verifier in the following ways:

    4.1 Gradle Project

    To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the +following sections:

    4.1.1 Prerequisites

    In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a Gradle or a Maven plugin.

    [Warning]Warning

    If you want to use Spock in your projects, you must add separately the spock-core and spock-spring modules. Check Spock docs for more information

    4.1.2 Add Gradle Plugin with Dependencies

    To add a Gradle plugin with dependencies, use code similar to this:

    buildscript {
    @@ -133,7 +133,8 @@ GroovyDSL. By default, its value is $rootDir/src/test/reso
     from the Groovy DSL should be placed. By default its value is
     $buildDir/generated-test-sources/contractVerifier.
  • stubsOutputDir: Specifies the directory where the generated WireMock stubs from the Groovy DSL should be placed.
  • targetFramework: Specifies the target test framework to be used. Currently, Spock and -JUnit are supported with JUnit being the default framework.
  • The following properties are used when you want to specify the location of the JAR +JUnit are supported with JUnit being the default framework.

  • contractsProperties: a map containing properties to be passed to Spring Cloud Contract +components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.
  • The following properties are used when you want to specify the location of the JAR containing the contracts: * contractDependency: Specifies the Dependency that provides groupid:artifactid:version:classifier coordinates. You can use the contractDependency @@ -141,9 +142,12 @@ closure to set it up. * contractsPath: Specifies the path to the jar. If contract dependencies are downloaded, the path defaults to groupid/artifactid where groupid is slash separated. Otherwise, it scans contracts under the provided directory. -* contractsWorkOffline: Specifies whether to download the dependencies each time, so -that you can work online. In other words, it specifies whether to reuses the local Maven -repo.

    4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base +* contractsMode: Specifies the mode of downloading contracts (whether the +JAR is available offline, remotely etc.) +* contractsSnapshotCheckSkip: If set to true will not assert whether the +downloaded stubs / contract JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact). +* deleteStubsAfterTest: If set to false will not remove any downloaded +contracts from temporary directories

    4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an endpoint, which should be verified.

    abstract class BaseMockMvcSpec extends Specification {
     
    @@ -181,7 +185,14 @@ baseClassMappings {
      - src/test/resources/contract/foo/

    By providing the baseClassForTests, we have a fallback in case mapping did not succeed. (You could also provide the packageWithBaseClasses as a fallback.) That way, the tests generated from src/test/resources/contract/com/ contracts extend the -com.example.ComBase, whereas the rest of the tests extend com.example.FooBase.

    4.1.12 Invoking Generated Tests

    To ensure that the provider side is compliant with defined contracts, you need to invoke:

    ./gradlew generateContractTests test

    4.1.13 Spring Cloud Contract Verifier on the Consumer Side

    In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin +com.example.ComBase, whereas the rest of the tests extend com.example.FooBase.

    4.1.12 Invoking Generated Tests

    To ensure that the provider side is compliant with defined contracts, you need to invoke:

    ./gradlew generateContractTests test

    4.1.13 Pushing stubs to SCM

    If you’re using the SCM repository to keep the contracts and +stubs, you might want to automate the step of pushing stubs to +the repository. To do that, it’s enough to call the pushStubsToScm +task. Example:

    $ ./gradlew pushStubsToScm

    Under Section 10.6, “Using the SCM Stub Downloader” you can find all possible +configuration options that you can pass either via +the contractsProperties field e.g. contracts { contractsProperties = [foo:"bar"] }, +via contractsProperties method e.g. contracts { contractsProperties([foo:"bar"]) }, +a system property or an environment variable.

    4.1.14 Spring Cloud Contract Verifier on the Consumer Side

    In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin in exactly the same way as in case of provider. If you do not want to use Stub Runner then you need to copy contracts stored in src/test/resources/contracts and generate WireMock JSON stubs using:

    ./gradlew generateClientStubs
    [Note]Note

    The stubsOutputDir option has to be set for stub generation to work.

    When present, JSON stubs can be used in automated tests of consuming a service.

    @ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)
    @@ -206,7 +217,7 @@ WireMock JSON stubs using:

    ./gradlew generateClie
      }
     }

    LoanApplication makes a call to FraudDetection service. This request is handled by a WireMock server configured with stubs generated by Spring Cloud Contract Verifier.

    4.2 Maven Project

    To learn how to set up the Maven project for Spring Cloud Contract Verifier, read the -following sections:

    4.2.1 Add maven plugin

    Add the Spring Cloud Contract BOM in a fashion similar to this:

    <dependencyManagement>
    +following sections:

    4.2.1 Add maven plugin

    Add the Spring Cloud Contract BOM in a fashion similar to this:

    <dependencyManagement>
     	<dependencies>
     		<dependency>
     			<groupId>org.springframework.cloud</groupId>
    @@ -225,8 +236,8 @@ following sections:

      <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> </configuration> </plugin>

    You can read more in the -Spring -Cloud Contract Maven Plugin Documentation.

    4.2.2 Maven and Rest Assured 2.0

    By default, Rest Assured 3.x is added to the classpath. However, you can use Rest +Spring +Cloud Contract Maven Plugin Documentation (example for 2.0.0.RELEASE version).

    4.2.2 Maven and Rest Assured 2.0

    By default, Rest Assured 3.x is added to the classpath. However, you can use Rest Assured 2.x by adding it to the plugins classpath, as shown here:

    <plugin>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    @@ -372,10 +383,12 @@ the matched contract. For example, if you have a contract under
     src/test/resources/contract/foo/bar/baz/ and map the property
     .* → com.example.base.BaseClass, then the test class generated from these contracts
     extends com.example.base.BaseClass. This setting takes precedence over
    -packageWithBaseClasses and baseClassForTests.

    If you want to download your contract definitions from a Maven repository, you can use +packageWithBaseClasses and baseClassForTests.

  • contractsProperties: a map containing properties to be passed to Spring Cloud Contract +components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.
  • If you want to download your contract definitions from a Maven repository, you can use the following options:

    • contractDependency: The contract dependency that contains all the packaged contracts.
    • contractsPath: The path to the concrete contracts in the JAR with packaged contracts. -Defaults to groupid/artifactid where gropuid is slash separated.
    • contractsWorkOffline: Dictates whether the dependencies should be downloaded or the -local Maven artifacts should be reused.
    • contractsRepositoryUrl: URL to a repo with the artifacts that have contracts. If it is not provided, +Defaults to groupid/artifactid where gropuid is slash separated.
    • contractsMode: Picks the mode in which stubs will be found and registered
    • contractsSnapshotCheckSkip: If true then will not assert whether a stub / contract +JAR was downloaded from local or remote location
    • deleteStubsAfterTest: If set to false will not remove any downloaded +contracts from temporary directories
    • contractsRepositoryUrl: URL to a repo with the artifacts that have contracts. If it is not provided, use the current Maven ones.
    • contractsRepositoryUsername: The user name to be used to connect to the repo with contracts.
    • contractsRepositoryPassword: The password to be used to connect to the repo with contracts.
    • contractsRepositoryProxyHost: The proxy host to be used to connect to the repo with contracts.
    • contractsRepositoryProxyPort: The proxy port to be used to connect to the repo with contracts.

    We cache only non-snapshot, explicitly provided versions (for example + or 1.0.0.BUILD-SNAPSHOT won’t get cached). By default, this feature is turned on.

    4.2.8 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an @@ -385,13 +398,51 @@ endpoint, which should be verified.

    import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc
     import spock.lang.Specification
     
    -class  MvcSpec extends Specification {
    +class MvcSpec extends Specification {
       def setup() {
        RestAssuredMockMvc.standaloneSetup(new ExampleSpringController())
       }
    -}

    If you use Explicit mode, you can use a base class to initialize the whole tested app -similarly, as you might find in regular integration tests. If you use the JAXRSCLIENT -mode, this base class should also contain a protected WebTarget webTarget field. Right +}

    You can also setup the whole context if necessary.

    import io.restassured.module.mockmvc.RestAssuredMockMvc;
    +import org.junit.Before;
    +import org.junit.runner.RunWith;
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.boot.test.context.SpringBootTest;
    +import org.springframework.test.context.junit4.SpringRunner;
    +import org.springframework.web.context.WebApplicationContext;
    +
    +@RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
    +public abstract class BaseTestClass {
    +
    +	@Autowired
    +	WebApplicationContext context;
    +
    +	@Before
    +	public void setup() {
    +		RestAssuredMockMvc.webAppContextSetup(this.context);
    +	}
    +}

    If you use EXPLICIT mode, you can use a base class to initialize the whole tested app +similarly, as you might find in regular integration tests.

    import io.restassured.RestAssured;
    +import org.junit.Before;
    +import org.junit.runner.RunWith;
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.boot.test.context.SpringBootTest;
    +import org.springframework.boot.web.server.LocalServerPort
    +import org.springframework.test.context.junit4.SpringRunner;
    +import org.springframework.web.context.WebApplicationContext;
    +
    +@RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
    +public abstract class BaseTestClass {
    +
    +	@LocalServerPort
    +	int port;
    +
    +	@Before
    +	public void setup() {
    +		RestAssured.baseURI = "http://localhost:" + this.port;
    +	}
    +}

    If you use the JAXRSCLIENT mode, this base class should also contain a protected WebTarget webTarget field. Right now, the only option to test the JAX-RS API is to start a web server.

    4.2.9 Different base classes for contracts

    If your base classes differ between contracts, you can tell the Spring Cloud Contract plugin which class should get extended by the autogenerated tests. You have two options:

    • Follow a convention by providing the packageWithBaseClasses
    • provide explicit mapping via baseClassMappings

    By Convention

    The convention is such that if you have a contract under (for example) src/test/resources/contract/foo/bar/baz/ and set the value of the @@ -455,7 +506,46 @@ goal.

    For Groovy Spock code, use the following:

    </testSources>
     	</configuration>
     </plugin>

    To ensure that provider side is compliant with defined contracts, you need to invoke -mvn generateTest test.

    4.2.11 Maven Plugin and STS

    If you see the following exception while using STS:

    STS Exception

    When you click on the error marker you should see something like this:

     plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
    +mvn generateTest test.

    4.2.11 Pushing stubs to SCM

    If you’re using the SCM repository to keep the contracts and +stubs, you might want to automate the step of pushing stubs to +the repository. To do that, it’s enough to add the pushStubsToScm +goal. Example:

    <plugin>
    +    <groupId>org.springframework.cloud</groupId>
    +    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +    <version>${spring-cloud-contract.version}</version>
    +    <extensions>true</extensions>
    +    <configuration>
    +        <!-- Base class mappings etc. -->
    +
    +        <!-- We want to pick contracts from a Git repository -->
    +        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>
    +
    +        <!-- We reuse the contract dependency section to set up the path
    +        to the folder that contains the contract definitions. In our case the
    +        path will be /groupId/artifactId/version/contracts -->
    +        <contractDependency>
    +            <groupId>${project.groupId}</groupId>
    +            <artifactId>${project.artifactId}</artifactId>
    +            <version>${project.version}</version>
    +        </contractDependency>
    +
    +        <!-- The contracts mode can't be classpath -->
    +        <contractsMode>REMOTE</contractsMode>
    +    </configuration>
    +    <executions>
    +        <execution>
    +            <phase>package</phase>
    +            <goals>
    +                <!-- By default we will not push the stubs back to SCM,
    +                you have to explicitly add it as a goal -->
    +                <goal>pushStubsToScm</goal>
    +            </goals>
    +        </execution>
    +    </executions>
    +</plugin>

    Under Section 10.6, “Using the SCM Stub Downloader” you can find all possible +configuration options that you can pass either via +the <configuration><contractProperties> map, a system property +or an environment variable.

    4.2.12 Maven Plugin and STS

    If you see the following exception while using STS:

    STS Exception

    When you click on the error marker you should see something like this:

     plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
      cloud-contract-maven-plugin:1.1.0.M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145) at
      org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:331) at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1362) at
     ...
    @@ -492,48 +582,7 @@ goal.

    For Groovy Spock code, use the following:

    </plugin>
             </plugins>
         </pluginManagement>
    -</build>

    4.2.12 Spring Cloud Contract Verifier on the Consumer Side

    You can also use the Spring Cloud Contract Verifier for the consumer side. To do so, use -the plugin so that it only converts the contracts and generates the stubs. To achieve -that, you need to configure Spring Cloud Contract Verifier plugin in exactly the same way -as you would for a provider. You need to copy contracts stored in -src/test/resources/contracts and generate WireMock JSON stubs using the -mvn generateStubs command. By default, the generated WireMock mapping is stored in a -directory named target/mappings. From these generated mappings, your project should -create additional artifacts with a classifier of stubs for easy deployment to the maven -repository.

    Here is a sample configuration:

    <plugin>
    -    <groupId>org.springframework.cloud</groupId>
    -    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    -    <version>${verifier-plugin.version}</version>
    -    <executions>
    -        <execution>
    -            <goals>
    -                <goal>convert</goal>
    -                <goal>generateStubs</goal>
    -            </goals>
    -        </execution>
    -    </executions>
    -</plugin>

    When present, JSON stubs can be used in consumer automated tests, as shown here:

    @RunWith(SpringTestRunner.class)
    -@SpringBootTest
    -@AutoConfigureStubRunner
    -public class LoanApplicationServiceTests {
    -
    -  @Autowired
    -  LoanApplicationService service;
    -
    -  @Test
    -  public void shouldSuccessfullyApplyForLoan() {
    -    //given:
    - 	LoanApplication application =
    -			new LoanApplication(new Client("12345678901"), 123.123);
    -    //when:
    -	LoanApplicationResult loanApplication = service.loanApplication(application);
    -    // then:
    -	assertThat(loanApplication.loanApplicationStatus).isEqualTo(LoanApplicationStatus.LOAN_APPLIED);
    -	assertThat(loanApplication.rejectionReason).isNull();
    -  }
    -}

    LoanApplication makes a call to the FraudDetection service. This request is handled -by a WireMock server configured with stubs generated by the Spring Cloud Contract -Verifier.

    4.3 Stubs and Transitive Dependencies

    The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One +</build>

    4.3 Stubs and Transitive Dependencies

    The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One problem that arises is that, when reusing the stubs, you can mistakenly import all of that stub’s dependencies. When building a Maven artifact, even though you have a couple of different jars, all of them share one pom:

    ├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar
    @@ -550,12 +599,99 @@ when you include the github-webhook stubs in anothe
     dependency gets downloaded by Stub Runner) then, since all of the dependencies are
     optional, they will not get downloaded.

    Create a separate artifactid for the stubs

    If you create a separate artifactid, then you can set it up in whatever way you wish. For example, you might decide to have no dependencies at all.

    Exclude dependencies on the consumer side

    As a consumer, if you add the stub dependency to your classpath, you can explicitly -exclude the unwanted dependencies.

    4.4 Scenarios

    You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to +exclude the unwanted dependencies.

    4.4 CI Server setup

    When fetching stubs / contracts in a CI, shared environment, what might happen is that +both the producer and the consumer reuse the same local Maven repository. Due to this, +the framework, responsible for downloading a stub JAR from remote location, +can’t decide which JAR should be picked, local or remote one. That caused +the "The artifact was found in the local repository but you have explicitly +stated that it should be downloaded from a remote one" exception +and failed the build.

    For such cases we’re introducing the property and plugin setup mechanism:

    • via stubrunner.snapshot-check-skip system property
    • via STUBRUNNER_SNAPSHOT_CHECK_SKIP environment variable

    if either of these values is set to true, then the stub downloader will not +verify the origin of the downloaded JAR.

    For the plugins you need to set the contractsSnapshotCheckSkip property +to true.

    4.5 Scenarios

    You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to stick to the proper naming convention while creating your contracts. The convention -requires including an order number followed by an underscore, as shown in this example:

    my_contracts_dir\
    +requires including an order number followed by an underscore. This will work regardles
    + of whether you’re working with YAML or Groovy. Example:

    my_contracts_dir\
       scenario1\
         1_login.groovy
         2_showCart.groovy
         3_logout.groovy

    Such a tree causes Spring Cloud Contract Verifier to generate WireMock’s scenario with a name of scenario1 and the three following steps:

    1. login marked as Started pointing to…​
    2. showCart marked as Step1 pointing to…​
    3. logout marked as Step2 which will close the scenario.

    More details about WireMock scenarios can be found at -http://wiremock.org/stateful-behaviour.html

    Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.

    \ No newline at end of file +http://wiremock.org/stateful-behaviour.html

    Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.

    4.6 Docker Project

    We’re publishing a springcloud/spring-cloud-contract Docker image +that contains a project that will generate tests and execute them in EXPLICIT mode +against a running application.

    [Tip]Tip

    The EXPLICIT mode means that the tests generated from contracts will send +real requests and not the mocked ones.

    4.6.1 Short intro to Maven, JARs and Binary storage

    Since the Docker image can be used by non JVM projects, it’s good to +explain the basic terms behind Spring Cloud Contract packaging defaults.

    Part of the following definitions were taken from the Maven Glossary

    • Project: Maven thinks in terms of projects. Everything that you +will build are projects. Those projects follow a well defined +“Project Object Model”. Projects can depend on other projects, +in which case the latter are called “dependencies”. A project may +consistent of several subprojects, however these subprojects are still +treated equally as projects.
    • Artifact: An artifact is something that is either produced or used +by a project. Examples of artifacts produced by Maven for a project +include: JARs, source and binary distributions. Each artifact +is uniquely identified by a group id and an artifact ID which is +unique within a group.
    • JAR: JAR stands for Java ARchive. It’s a format based on +the ZIP file format. Spring Cloud Contract packages the contracts and generated +stubs in a JAR file.
    • GroupId: A group ID is a universally unique identifier for a project. +While this is often just the project name (eg. commons-collections), +it is helpful to use a fully-qualified package name to distinguish it +from other projects with a similar name (eg. org.apache.maven). +Typically, when published to the Artifact Manager, the GroupId will get +slash separated and form part of the URL. E.g. for group id com.example +and artifact id application would be /com/example/application/.
    • Classifier: The Maven dependency notation looks as follows: +groupId:artifactId:version:classifier. The classifier is additional suffix +passed to the dependency. E.g. stubs, sources. The same dependency +e.g. com.example:application can produce multiple artifacts that +differ from each other with the classifier.
    • Artifact manager: When you generate binaries / sources / packages, you would +like them to be available for others to download / reference or reuse. In case +of the JVM world those artifacts would be JARs, for Ruby these are gems +and for Docker those would be Docker images. You can store those artifacts +in a manager. Examples of such managers can be Artifactory +or Nexus.

    4.6.2 How it works

    The image searches for contracts under the /contracts folder. +The output from running the tests will be available under +/spring-cloud-contract/build folder (it’s useful for debugging +purposes).

    It’s enough for you to mount your contracts, pass the environment variables + and the image will:

    • generate the contract tests
    • execute the tests against the provided URL
    • generate the WireMock stubs
    • (optional - turned on by default) publish the stubs to a Artifact Manager

    Environment Variables

    The Docker image requires some environment variables to point to +your running application, to the Artifact manager instance etc.

    • PROJECT_GROUP - your project’s group id. Defaults to com.example
    • PROJECT_VERSION - your project’s version. Defaults to 0.0.1-SNAPSHOT
    • PROJECT_NAME - artifact id. Defaults to example
    • REPO_WITH_BINARIES_URL - URL of your Artifact Manager. Defaults to http://localhost:8081/artifactory/libs-release-local +which is the default URL of Artifactory running locally
    • REPO_WITH_BINARIES_USERNAME - (optional) username when the Artifact Manager is secured
    • REPO_WITH_BINARIES_PASSWORD - (optional) password when the Artifact Manager is secured
    • PUBLISH_ARTIFACTS - if set to true then will publish artifact to binary storage. Defaults to true.

    These environment variables are used when contracts lay in an external repository. To enable +this feature you must set the EXTERNAL_CONTRACTS_ARTIFACT_ID environment variable.

    • EXTERNAL_CONTRACTS_GROUP_ID - group id of the project with contracts. Defaults to com.example
    • EXTERNAL_CONTRACTS_ARTIFACT_ID- artifact id of the project with contracts.
    • EXTERNAL_CONTRACTS_CLASSIFIER- classifier of the project with contracts. Empty by default
    • EXTERNAL_CONTRACTS_VERSION - version of the project with contracts. Defaults to +, equivalent to picking the latest
    • EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL - URL of your Artifact Manager. Defaults to value of REPO_WITH_BINARIES_URL env var. +If that’s not set, defaults to http://localhost:8081/artifactory/libs-release-local +which is the default URL of Artifactory running locally
    • EXTERNAL_CONTRACTS_PATH - path to contracts for the given project, inside the project with contracts. +Defaults to slash separated EXTERNAL_CONTRACTS_GROUP_ID concatenated with / and EXTERNAL_CONTRACTS_ARTIFACT_ID. E.g. +for group id foo.bar and artifact id baz, would result in foo/bar/baz contracts path.
    • EXTERNAL_CONTRACTS_WORK_OFFLINE - if set to true then will retrieve artifact with contracts +from the container’s .m2. Mount your local .m2 as a volume available at the container’s /root/.m2 path. +You must not set both EXTERNAL_CONTRACTS_WORK_OFFLINE and EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL.

    These environment variables are used when tests are executed:

    • APPLICATION_BASE_URL - url against which tests should be executed. +Remember that it has to be accessible from the Docker container (e.g. localhost +will not work)
    • APPLICATION_USERNAME - (optional) username for basic authentication to your application
    • APPLICATION_PASSWORD - (optional) password for basic authentication to your application

    4.6.3 Example of usage

    Let’s take a look at a simple MVC application

    $ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
    +$ cd bookstore

    The contracts are available under /contracts folder.

    4.6.4 Server side (nodejs)

    Since we want to run tests, we could just execute:

    $ npm test

    however, for learning purposes, let’s split it into pieces:

    # Stop docker infra (nodejs, artifactory)
    +$ ./stop_infra.sh
    +# Start docker infra (nodejs, artifactory)
    +$ ./setup_infra.sh
    +
    +# Kill & Run app
    +$ pkill -f "node app"
    +$ nohup node app &
    +
    +# Prepare environment variables
    +$ SC_CONTRACT_DOCKER_VERSION="..."
    +$ APP_IP="192.168.0.100"
    +$ APP_PORT="3000"
    +$ ARTIFACTORY_PORT="8081"
    +$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
    +$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
    +$ CURRENT_DIR="$( pwd )"
    +$ CURRENT_FOLDER_NAME=${PWD##*/}
    +$ PROJECT_VERSION="0.0.1.RELEASE"
    +
    +# Execute contract tests
    +$ docker run  --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
    +
    +# Kill app
    +$ pkill -f "node app"

    What will happen is that via bash scripts:

    • infrastructure will be set up (MongoDb, Artifactory). +In real life scenario you would just run the NodeJS application +with mocked database. In this example we want to show how we can +benefit from Spring Cloud Contract in no time.
    • due to those constraints the contracts also represent the +stateful situation

      • first request is a POST that causes data to get inserted to the database
      • second request is a GET that returns a list of data with 1 previously inserted element
    • the NodeJS application will be started (on port 3000)
    • contract tests will be generated via Docker and tests +will be executed against the running application

      • the contracts will be taken from /contracts folder.
      • the output of the test execution is available under +node_modules/spring-cloud-contract/output.
    • the stubs will be uploaded to Artifactory. You can check them out +under http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ . +The stubs will be here http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar.

    To see how the client side looks like check out the Section 6.9, “Stub Runner Docker” section.

    \ No newline at end of file diff --git a/2.0.x/multi/multi__spring_cloud_contract_wiremock.html b/2.0.x/multi/multi__spring_cloud_contract_wiremock.html index 608a4d8d95..a0a82e14d5 100644 --- a/2.0.x/multi/multi__spring_cloud_contract_wiremock.html +++ b/2.0.x/multi/multi__spring_cloud_contract_wiremock.html @@ -2,10 +2,10 @@ 11. Spring Cloud Contract WireMock

    11. Spring Cloud Contract WireMock

    The Spring Cloud Contract WireMock modules let you use WireMock in a Spring Boot application. Check out the -samples +samples for more details.

    If you have a Spring Boot application that uses Tomcat as an embedded server (which is the default with spring-boot-starter-web), you can add -spring-cloud-contract-wiremock to your classpath and add @AutoConfigureWireMock in +spring-cloud-starter-contract-stub-runner to your classpath and add @AutoConfigureWireMock in order to be able to use Wiremock in your tests. Wiremock runs as a stub server and you can register stub behavior using a Java API or via static JSON declarations as part of your test. The following code shows an example:

    @RunWith(SpringRunner.class)
    @@ -112,7 +112,8 @@ annotation or the stub runner. If you use the JUnit @Rule<
     classpath and it is selected by the RestTemplateBuilder and configured to ignore SSL
     errors. If you use the default java.net client, you do not need the annotation (but it
     won’t do any harm). There is no support currently for other clients, but it may be added
    -in future releases.

    11.5 WireMock and Spring MVC Mocks

    Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into +in future releases.

    To disable the custom RestTemplateBuilder, set the wiremock.rest-template-ssl-enabled +property to false.

    11.5 WireMock and Spring MVC Mocks

    Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a Spring MockRestServiceServer. The following code shows an example:

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment = WebEnvironment.NONE)
     public class WiremockForDocsMockServerApplicationTests {
    @@ -143,13 +144,22 @@ pattern. The JSON format is the normal WireMock format, which you can read about
     WireMock website.

    Currently, the Spring Cloud Contract Verifier supports Tomcat, Jetty, and Undertow as Spring Boot embedded servers, and Wiremock itself has "native" support for a particular version of Jetty (currently 9.2). To use the native Jetty, you need to add the native -Wiremock dependencies and exclude the Spring Boot container (if there is one).

    11.6 Generating Stubs using REST Docs

    Spring REST Docs can be used to generate -documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc or +Wiremock dependencies and exclude the Spring Boot container (if there is one).

    11.6 Customization of WireMock configuration

    You can register a bean of org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer type +in order to customize the WireMock configuration (e.g. add custom transformers). +Example:

    		@Bean WireMockConfigurationCustomizer optionsCustomizer() {
    +			return new WireMockConfigurationCustomizer() {
    +				@Override public void customize(WireMockConfiguration options) {
    +// perform your customization here
    +				}
    +			};
    +		}

    11.7 Generating Stubs using REST Docs

    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 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 -example:

    @RunWith(SpringRunner.class)
    +example using MockMvc:

    @RunWith(SpringRunner.class)
     @SpringBootTest
     @AutoConfigureRestDocs(outputDir = "target/snippets")
     @AutoConfigureMockMvc
    @@ -165,32 +175,49 @@ example:

    "resource"));
     	}
     }

    This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches -all GET requests to the "/resource" path.

    Without any additional configuration, this tests creates a stub with a request matcher +all GET requests to the "/resource" path. The same example with WebTestClient (used +for testing Spring WebFlux applications) would look like this:

    @RunWith(SpringRunner.class)
    +@SpringBootTest
    +@AutoConfigureRestDocs(outputDir = "target/snippets")
    +@AutoConfigureWebTestClient
    +public class ApplicationTests {
    +
    +	@Autowired
    +	private WebTestClient client;
    +
    +	@Test
    +	public void contextLoads() throws Exception {
    +		client.get().uri("/resource").exchange()
    +				.expectBody(String.class).isEqualTo("Hello World")
    + 				.consumeWith(document("resource"));
    +	}
    +}

    Without any additional configuration, these tests create a stub with a request matcher for the HTTP method and all headers except "host" and "content-length". To match the request more precisely (for example, to match the body of a POST or PUT), we need to explicitly create a request matcher. Doing so has two effects:

    • Creating a stub that matches only in the way you specify.
    • Asserting that the request in the test case also matches the same conditions.

    The main entry point for this feature is WireMockRestDocs.verify(), which can be used as a substitute for the document() convenience method, as shown in the following -example:

    @RunWith(SpringRunner.class)
    -@SpringBootTest
    -@AutoConfigureRestDocs(outputDir = "target/snippets")
    -@AutoConfigureMockMvc
    -public class ApplicationTests {
    +example:

    import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
    @RunWith(SpringRunner.class)
    +@SpringBootTest
    +@AutoConfigureRestDocs(outputDir = "target/snippets")
    +@AutoConfigureMockMvc
    +public class ApplicationTests {
     
    -	@Autowired
    -	private MockMvc mockMvc;
    +	@Autowired
    +	private MockMvc mockMvc;
     
    -	@Test
    -	public void contextLoads() throws Exception {
    -		mockMvc.perform(post("/resource")
    -                .content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
    +	@Test
    +	public void contextLoads() throws Exception {
    +		mockMvc.perform(post("/resource")
    +                .content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
     				.andExpect(status().isOk())
    -				.andDo(verify().jsonPath("$.id")
    -                        .stub("resource"));
    +				.andDo(verify().jsonPath("$.id")
    +                        .stub("resource"));
     	}
     }

    This contract specifies that any valid POST with an "id" field receives the response defined in this test. You can chain together calls to .jsonPath() to add additional matchers. If JSON Path is unfamiliar, The JayWay -documentation can help you get up to speed.

    Instead of the jsonPath and contentType convenience methods, you can also use the +documentation can help you get up to speed. The WebTestClient version of this test +has a similar verify() static helper that you insert in the same place.

    Instead of the jsonPath and contentType convenience methods, you can also use the WireMock APIs to verify that the request matches the created stub, as shown in the following example:

    @Test
     public void contextLoads() throws Exception {
    @@ -225,11 +252,10 @@ range of parameters. The above example generates a stub resembling the following
     

    [Note]Note

    You can use either the wiremock() method or the jsonPath() and contentType() methods to create request matchers, but you can’t use both approaches.

    On the consumer side, you can make the resource.json generated earlier in this section available on the classpath (by -publishing -stubs as JARs, for example). After that, you can create a stub using WireMock in a +<<publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a number of different ways, including by using @AutoConfigureWireMock(stubs="classpath:resource.json"), as described earlier in this -document.

    11.7 Generating Contracts by Using REST Docs

    You can also generate Spring Cloud Contract DSL files and documentation with Spring REST +document.

    11.8 Generating Contracts by Using REST Docs

    You can also generate Spring Cloud Contract DSL files and documentation with Spring REST Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts and the stubs.

    Why would you want to use this feature? Some people in the community asked questions about a situation in which they would like to move to DSL-based contract definition, @@ -240,12 +266,13 @@ is there because it makes sense to generate both the contracts and the stubs.

    "{\"foo\": 23 }")) + .content("{\"foo\": 23, \"bar\" : \"baz\" }")) .andExpect(status().isOk()) .andExpect(content().string("bar")) // first WireMock .andDo(WireMockRestDocs.verify() .jsonPath("$[?(@.foo >= 20)]") + .jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]") .contentType(MediaType.valueOf("application/json")) .stub("shouldGrantABeerIfOldEnough")) // then Contract DSL documentation @@ -265,7 +292,7 @@ Contract.make { } } response { - status 200 + status OK() body(''' bar ''') diff --git a/2.0.x/multi/multi__stub_runner_for_messaging.html b/2.0.x/multi/multi__stub_runner_for_messaging.html index cfa743cbc3..8186a652d9 100644 --- a/2.0.x/multi/multi__stub_runner_for_messaging.html +++ b/2.0.x/multi/multi__stub_runner_for_messaging.html @@ -184,7 +184,7 @@ property.

    Assume that you have the following Maven repository with a deplo } }

    Now consider the following Spring configuration:

    stubrunner.repositoryRoot: classpath:m2repo/repository/
     stubrunner.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs
    -
    +stubrunner.stubs-mode: remote
     spring:
       cloud:
         stream:
    @@ -261,6 +261,7 @@ to disable them explicitly by setting the  stubrunner.stre
     }

    Now consider the following Spring configuration:

    stubrunner:
       repositoryRoot: classpath:m2repo/repository/
       ids: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:0.4.0-SNAPSHOT:stubs
    +  stubs-mode: remote
       amqp:
         enabled: true
     server:
    diff --git a/2.0.x/multi/multi__using_the_pluggable_architecture.html b/2.0.x/multi/multi__using_the_pluggable_architecture.html
    index 6879bb1be8..a22c482ac3 100644
    --- a/2.0.x/multi/multi__using_the_pluggable_architecture.html
    +++ b/2.0.x/multi/multi__using_the_pluggable_architecture.html
    @@ -5,19 +5,7 @@ such as YAML, RAML or PACT. In those cases, you still want to benefit from the a
     generation of tests and stubs. You can add your own implementation for generating both
     tests and stubs. Also, you can customize the way tests are generated (for example, you
     can generate tests for other languages) and the way stubs are generated (for example, you
    -can generate stubs for other HTTP server implementations).

    10.1 Custom Contract Converter

    Assume that your contract is written in a YAML file as follows:

    request:
    -  url: /foo
    -  method: PUT
    -  headers:
    -    foo: bar
    -  body:
    -    foo: bar
    -response:
    -  status: 200
    -  headers:
    -    foo2: bar
    -  body:
    -    foo2: bar

    The ContractConverter interface lets you register your own implementation of a contract +can generate stubs for other HTTP server implementations).

    10.1 Custom Contract Converter

    The ContractConverter interface lets you register your own implementation of a contract structure converter. The following code listing shows the ContractConverter interface:

    package org.springframework.cloud.contract.spec
     
     /**
    @@ -58,89 +46,16 @@ structure converter. The following code listing shows the 
     }

    Your implementation must define the condition on which it should start the conversion. Also, you must define how to perform that conversion in both directions.

    [Important]Important

    Once you create your implementation, you must create a /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:

    # Converters
    -org.springframework.cloud.contract.spec.ContractConverter=\
    -org.springframework.cloud.contract.verifier.converter.YamlContractConverter
    -
    -# tag::extension[]
    -org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\
    -org.springframework.cloud.contract.verifier.dsl.wiremock.TestWireMockExtensions
    -# end::extension[]

    The following example shows a typical YAML implementation that matches the preceding -example:

    package org.springframework.cloud.contract.verifier.converter
    -
    -import java.nio.file.Files
    -
    -import groovy.transform.CompileStatic
    -import org.springframework.cloud.contract.spec.Contract
    -import org.springframework.cloud.contract.spec.ContractConverter
    -import org.springframework.cloud.contract.spec.internal.Headers
    -import org.yaml.snakeyaml.Yaml
    -
    -/**
    - * Simple converter from and to a {@link YamlContract} to a collection of {@link Contract}
    - */
    -@CompileStatic
    -class YamlContractConverter implements ContractConverter<List<YamlContract>> {
    -
    -	@Override
    -	public boolean isAccepted(File file) {
    -		String name = file.getName()
    -		return name.endsWith(".yml") || name.endsWith(".yaml")
    -	}
    -
    -	@Override
    -	public Collection<Contract> convertFrom(File file) {
    -		try {
    -			YamlContract yamlContract = new Yaml().loadAs(
    -					Files.newInputStream(file.toPath()), YamlContract.class)
    -			return [Contract.make {
    -				request {
    -					method(yamlContract?.request?.method)
    -					url(yamlContract?.request?.url)
    -					headers {
    -						yamlContract?.request?.headers?.each { String key, Object value ->
    -							header(key, value)
    -						}
    -					}
    -					body(yamlContract?.request?.body)
    -				}
    -				response {
    -					status(yamlContract?.response?.status)
    -					headers {
    -						yamlContract?.response?.headers?.each { String key, Object value ->
    -							header(key, value)
    -						}
    -					}
    -					body(yamlContract?.response?.body)
    -				}
    -			}]
    -		}
    -		catch (FileNotFoundException e) {
    -			throw new IllegalStateException(e)
    -		}
    -	}
    -
    -	@Override
    -	public List<YamlContract> convertTo(Collection<Contract> contracts) {
    -		return contracts.collect { Contract contract ->
    -			YamlContract yamlContract = new YamlContract()
    -			yamlContract.request.with {
    -				method = contract?.request?.method?.clientValue
    -				url = contract?.request?.url?.clientValue
    -				headers = (contract?.request?.headers as Headers)?.asStubSideMap()
    -				body = contract?.request?.body?.clientValue as Map
    -			}
    -			yamlContract.response.with {
    -				status = contract?.response?.status?.clientValue as Integer
    -				headers = (contract?.response?.headers as Headers)?.asStubSideMap()
    -				body = contract?.response?.body?.clientValue as Map
    -			}
    -			return yamlContract
    -		}
    -	}
    -}

    10.1.1 Pact Converter

    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 +implementation.

    The following example shows a typical spring.factories file:

    org.springframework.cloud.contract.spec.ContractConverter=\
    +org.springframework.cloud.contract.verifier.converter.YamlContractConverter

    10.1.1 Pact Converter

    Spring Cloud Contract includes support for Pact representation of +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"
    @@ -161,10 +76,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" + } } } }, @@ -178,9 +119,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" + } } } } @@ -188,13 +147,13 @@ 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" } } -}

    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>
    @@ -208,19 +167,13 @@ the current Pact version that you use.

    Maven.  <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-contract-spec-pact</artifactId> + <artifactId>spring-cloud-contract-pact</artifactId> <version>${spring-cloud-contract.version}</version> </dependency> - <dependency> - <groupId>au.com.dius</groupId> - <artifactId>pact-jvm-model</artifactId> - <version>2.4.18</version> - </dependency> </dependencies> </plugin>

    Gradle.  -

    classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
    -classpath 'au.com.dius:pact-jvm-model:2.4.18'

    +

    classpath "org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}"

    When you execute the build of your application, a test will be generated. The generated test might be as follows:

    @Test
     public void validate_shouldMarkClientAsFraud() throws Exception {
    @@ -235,24 +188,25 @@ test might be as follows:

    // 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})/)]"
         } ]
    @@ -262,25 +216,19 @@ test might be as follows:

    "body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
         "headers" : {
           "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
    -    }
    -  }
    +    },
    +    "transformers" : [ "response-template" ]
    +  },
     }

    10.1.4 Pact for Consumers

    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. 

    <dependency>
     	<groupId>org.springframework.cloud</groupId>
    -	<artifactId>spring-cloud-contract-spec-pact</artifactId>
    -	<scope>test</scope>
    -</dependency>
    -<dependency>
    -	<groupId>au.com.dius</groupId>
    -	<artifactId>pact-jvm-model</artifactId>
    -	<version>2.4.18</version>
    +	<artifactId>spring-cloud-contract-pact</artifactId>
     	<scope>test</scope>
     </dependency>

    Gradle.  -

    testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
    -testCompile 'au.com.dius:pact-jvm-model:2.4.18'

    +

    testCompile "org.springframework.cloud:spring-cloud-contract-pact"

    10.2 Using the Custom Test Generator

    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
    @@ -464,8 +412,45 @@ implementation is used. If you provide more than one, the first one on the list
     }

    Then you can register it in your spring.factories file, as shown in the following example:

    # Example of a custom Stub Downloader Provider
     org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
    -com.example.CustomStubDownloaderBuilder

    Now you can pick a folder with the source of your stubs.

    [Important]Important

    If you do not provide any implementation, then the default is used. -If you use the repositoryRoot property or the workOffline flag, then an Aether-based -implementation that downloads stubs from a remote repository is used. If you do not -provide these values, the ClasspathStubProvider (which will scan the classpath) is -used. If you provide more than one, then the first one on the list is used.

    \ No newline at end of file +com.example.CustomStubDownloaderBuilder

    Now you can pick a folder with the source of your stubs.

    [Important]Important

    If you do not provide any implementation, then the default is used (scan classpath). +If you provide the stubsMode = StubRunnerProperties.StubsMode.LOCAL or +, stubsMode = StubRunnerProperties.StubsMode.REMOTE then the Aether implementation will be used +If you provide more than one, then the first one on the list is used.

    10.6 Using the SCM Stub Downloader

    Whenever the repositoryRoot starts with a SCM protocol +(currently we support only git://), the stub downloader will try +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

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop) +

    * stubrunner.properties.git.branch (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop) +

    * stubrunner.properties.git.username (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop) +

    * stubrunner.properties.git.password (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop) +

    * stubrunner.properties.git.no-of-attempts (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop) +

    * stubrunner.properties.git.wait-between-attempts (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol +(starts with pact://), the stub downloader will try +to fetch the Pact contract definitions from the Pact Broker. +Whatever is set after pact:// will be parsed as the Pact Broker URL.

    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

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop) +

    * stubrunner.properties.pactbroker.host (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop) +

    * stubrunner.properties.pactbroker.port (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop) +

    * stubrunner.properties.pactbroker.protocol (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop) +

    * stubrunner.properties.pactbroker.tags (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop) +

    * stubrunner.properties.pactbroker.auth.scheme (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop) +

    * stubrunner.properties.pactbroker.auth.username (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop) +

    * stubrunner.properties.pactbroker.auth.password (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop) +

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    \ No newline at end of file diff --git a/2.0.x/multi/multi_pr01.html b/2.0.x/multi/multi_pr01.html index 3436568f42..f58f0ab5dc 100644 --- a/2.0.x/multi/multi_pr01.html +++ b/2.0.x/multi/multi_pr01.html @@ -1,4 +1,4 @@

    Documentation Authors: Adam Dudczak, Mathias Düsterhöft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubryński, Karol Lassak, -Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer, Jay Bryant

    2.0.0.BUILD-SNAPSHOT

    \ No newline at end of file +Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer, Jay Bryant

    2.0.1.BUILD-SNAPSHOT

    \ No newline at end of file diff --git a/2.0.x/multi/multi_spring-cloud-contract.html b/2.0.x/multi/multi_spring-cloud-contract.html index fadbbf1b9c..2cc2a1a3f5 100644 --- a/2.0.x/multi/multi_spring-cloud-contract.html +++ b/2.0.x/multi/multi_spring-cloud-contract.html @@ -1,3 +1,3 @@ - Spring Cloud Contract

    Spring Cloud Contract


    Table of Contents

    1. Spring Cloud Contract
    2. Spring Cloud Contract Verifier Introduction
    2.1. Why a Contract Verifier?
    2.1.1. Testing issues
    2.2. Purposes
    2.3. How It Works
    2.3.1. Defining the contract
    2.3.2. Client Side
    2.3.3. Server Side
    2.4. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.4.1. Technical note
    2.4.2. Consumer side (Loan Issuance)
    2.4.3. Producer side (Fraud Detection server)
    2.4.4. Consumer Side (Loan Issuance) Final Step
    2.5. Dependencies
    2.6. Additional Links
    2.6.1. Spring Cloud Contract video
    2.6.2. Readings
    2.7. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. What is this value(consumer(), producer()) ?
    3.3. How to do Stubs versioning?
    3.3.1. API Versioning
    3.3.2. JAR versioning
    3.3.3. Dev or prod stubs
    3.4. Common repo with contracts
    3.4.1. Repo structure
    3.4.2. Workflow
    3.4.3. Consumer
    3.4.4. Producer
    3.5. Can I have multiple base classes for tests?
    3.6. How can I debug the request/response being sent by the generated tests client?
    3.6.1. How can I debug the mapping/request/response being sent by WireMock?
    3.6.2. How can I see what got registered in the HTTP server stub?
    3.6.3. Can I reference the request from the response?
    3.6.4. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Maven Plugin and STS
    4.2.12. Spring Cloud Contract Verifier on the Consumer Side
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Limitations
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Integration
    7.2.1. Adding the Runner to the Project
    7.2.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Stream
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Spring AMQP
    7.4.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Messaging Top-Level Elements
    8.9.1. Output Triggered by a Method
    8.9.2. Output Triggered by a Message
    8.9.3. Consumer/Producer
    8.9.4. Common
    8.10. Multiple Contracts in One File
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Generating Stubs using REST Docs
    11.7. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    12.3.1. No Camel support
    13. Links
    \ No newline at end of file + Spring Cloud Contract

    Spring Cloud Contract


    Table of Contents

    1. Spring Cloud Contract
    2. Spring Cloud Contract Verifier Introduction
    2.1. Why a Contract Verifier?
    2.1.1. Testing issues
    2.2. Purposes
    2.3. How It Works
    2.3.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.3.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.3.3. Defining the Contract
    2.3.4. Client Side
    2.3.5. Server Side
    2.4. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.4.1. Technical note
    2.4.2. Consumer side (Loan Issuance)
    2.4.3. Producer side (Fraud Detection server)
    2.4.4. Consumer Side (Loan Issuance) Final Step
    2.5. Dependencies
    2.6. Additional Links
    2.6.1. Spring Cloud Contract video
    2.6.2. Readings
    2.7. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    3.6.3. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.3. Stubs and Transitive Dependencies
    4.4. CI Server setup
    4.5. Scenarios
    4.6. Docker Project
    4.6.1. Short intro to Maven, JARs and Binary storage
    4.6.2. How it works
    Environment Variables
    4.6.3. Example of usage
    4.6.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Limitations
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Integration
    7.2.1. Adding the Runner to the Project
    7.2.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Stream
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Spring AMQP
    7.4.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    12.3.1. No Camel support
    13. Links
    \ No newline at end of file diff --git a/2.0.x/single/images/callouts/1.png b/2.0.x/single/images/callouts/1.png new file mode 100644 index 0000000000..7d473430b7 Binary files /dev/null and b/2.0.x/single/images/callouts/1.png differ diff --git a/2.0.x/single/images/callouts/2.png b/2.0.x/single/images/callouts/2.png new file mode 100644 index 0000000000..5d09341b2f Binary files /dev/null and b/2.0.x/single/images/callouts/2.png differ diff --git a/2.0.x/single/images/callouts/3.png b/2.0.x/single/images/callouts/3.png new file mode 100644 index 0000000000..ef7b700471 Binary files /dev/null and b/2.0.x/single/images/callouts/3.png differ diff --git a/2.0.x/single/spring-cloud-contract.html b/2.0.x/single/spring-cloud-contract.html index 587087b5ab..b5b3e12d46 100644 --- a/2.0.x/single/spring-cloud-contract.html +++ b/2.0.x/single/spring-cloud-contract.html @@ -1,7 +1,7 @@ - Spring Cloud Contract

    Spring Cloud Contract


    Table of Contents

    1. Spring Cloud Contract
    2. Spring Cloud Contract Verifier Introduction
    2.1. Why a Contract Verifier?
    2.1.1. Testing issues
    2.2. Purposes
    2.3. How It Works
    2.3.1. Defining the contract
    2.3.2. Client Side
    2.3.3. Server Side
    2.4. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.4.1. Technical note
    2.4.2. Consumer side (Loan Issuance)
    2.4.3. Producer side (Fraud Detection server)
    2.4.4. Consumer Side (Loan Issuance) Final Step
    2.5. Dependencies
    2.6. Additional Links
    2.6.1. Spring Cloud Contract video
    2.6.2. Readings
    2.7. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. What is this value(consumer(), producer()) ?
    3.3. How to do Stubs versioning?
    3.3.1. API Versioning
    3.3.2. JAR versioning
    3.3.3. Dev or prod stubs
    3.4. Common repo with contracts
    3.4.1. Repo structure
    3.4.2. Workflow
    3.4.3. Consumer
    3.4.4. Producer
    3.5. Can I have multiple base classes for tests?
    3.6. How can I debug the request/response being sent by the generated tests client?
    3.6.1. How can I debug the mapping/request/response being sent by WireMock?
    3.6.2. How can I see what got registered in the HTTP server stub?
    3.6.3. Can I reference the request from the response?
    3.6.4. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Maven Plugin and STS
    4.2.12. Spring Cloud Contract Verifier on the Consumer Side
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Limitations
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Integration
    7.2.1. Adding the Runner to the Project
    7.2.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Stream
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Spring AMQP
    7.4.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Messaging Top-Level Elements
    8.9.1. Output Triggered by a Method
    8.9.2. Output Triggered by a Message
    8.9.3. Consumer/Producer
    8.9.4. Common
    8.10. Multiple Contracts in One File
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Generating Stubs using REST Docs
    11.7. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    12.3.1. No Camel support
    13. Links

    Documentation Authors: Adam Dudczak, Mathias Düsterhöft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubryński, Karol Lassak, -Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer, Jay Bryant

    2.0.0.BUILD-SNAPSHOT

    1. Spring Cloud Contract

    You need confidence when pushing new features to a new application or service in a + Spring Cloud Contract

    Spring Cloud Contract


    Table of Contents

    1. Spring Cloud Contract
    2. Spring Cloud Contract Verifier Introduction
    2.1. Why a Contract Verifier?
    2.1.1. Testing issues
    2.2. Purposes
    2.3. How It Works
    2.3.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.3.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.3.3. Defining the Contract
    2.3.4. Client Side
    2.3.5. Server Side
    2.4. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.4.1. Technical note
    2.4.2. Consumer side (Loan Issuance)
    2.4.3. Producer side (Fraud Detection server)
    2.4.4. Consumer Side (Loan Issuance) Final Step
    2.5. Dependencies
    2.6. Additional Links
    2.6.1. Spring Cloud Contract video
    2.6.2. Readings
    2.7. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    3.6.3. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.3. Stubs and Transitive Dependencies
    4.4. CI Server setup
    4.5. Scenarios
    4.6. Docker Project
    4.6.1. Short intro to Maven, JARs and Binary storage
    4.6.2. How it works
    Environment Variables
    4.6.3. Example of usage
    4.6.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Limitations
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Integration
    7.2.1. Adding the Runner to the Project
    7.2.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Stream
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Spring AMQP
    7.4.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    12.3.1. No Camel support
    13. Links

    Documentation Authors: Adam Dudczak, Mathias Düsterhöft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubryński, Karol Lassak, +Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer, Jay Bryant

    2.0.1.BUILD-SNAPSHOT

    1. Spring Cloud Contract

    You need confidence when pushing new features to a new application or service in a distributed system. This project provides support for Consumer Driven Contracts and service schemas in Spring applications (for both HTTP and message-based interactions), covering a range of options for writing tests, publishing them as assets, and asserting @@ -14,14 +14,14 @@ produced by Spring Cloud Contract Verifier.

  • Messaging r Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your own integrations.
  • Acceptance tests (in JUnit or Spock) are used to verify if server-side implementation of the API is compliant with the contract (server tests). A full test is generated by -Spring Cloud Contract Verifier.
  • 2.1 Why a Contract Verifier?

    Assume that we have a system consisting of multiple microservices:

    Microservices Architecture

    2.1.1 Testing issues

    If we wanted to test the application in top left corner to determine whether it can +Spring Cloud Contract Verifier.

    2.1 Why a Contract Verifier?

    Assume that we have a system consisting of multiple microservices:

    Microservices Architecture

    2.1.1 Testing issues

    If we wanted to test the application in top left corner to determine whether it can communicate with other services, we could do one of two things:

    • Deploy all microservices and perform end-to-end tests.
    • Mock other microservices in unit/integration tests.

    Both have their advantages but also a lot of disadvantages.

    Deploy all microservices and perform end to end tests

    Advantages:

    • Simulates production.
    • Tests real communication between services.

    Disadvantages:

    • To test one microservice, we have to deploy 6 microservices, a couple of databases, etc.
    • The environment where the tests run is locked for a single suite of tests (nobody else would be able to run the tests in the meantime).
    • They take a long time to run.
    • The feedback comes very late in the process.
    • They are extremely hard to debug.

    Mock other microservices in unit/integration tests

    Advantages:

    • They provide very fast feedback.
    • They have no infrastructure requirements.

    Disadvantages:

    • The implementor of the service creates stubs that might have nothing to do with reality.
    • You can go to production with passing tests and failing production.

    To solve the aforementioned issues, Spring Cloud Contract Verifier with Stub Runner was created. The main idea is to give you very fast feedback, without the need to set up the whole world of microservices. If you work on stubs, then the only applications you need -are those that your application directly uses.

    Stubbed Services

    Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were +are those that your application directly uses.

    Stubbed Services

    Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were created by the service that you’re calling. Also, if you can use them, it means that they were tested against the producer’s side. In short, you can trust those stubs.

    2.2 Purposes

    The main purposes of Spring Cloud Contract Verifier with Stub Runner are:

    • To ensure that WireMock/Messaging stubs (used when developing the client) do exactly what the actual server-side implementation does.
    • To promote ATDD method and Microservices architectural style.
    • To provide a way to publish changes in contracts that are immediately visible on both @@ -29,10 +29,227 @@ sides.
    • To generate boilerplate test code to be used on features in the contracts. Assume that we have a business use case of fraud check. If a user can be a fraud for 100 different reasons, we would assume that you would create 2 contracts, one for the positive case and one for the negative case. Contract tests are -used to test contracts between applications and not to simulate full behavior.

    2.3 How It Works

    This section explores how Spring Cloud Contract Verifier with Stub Runner works.

    2.3.1 Defining the contract

    As consumers of services, we need to define what exactly we want to achieve. We need to +used to test contracts between applications and not to simulate full behavior.

    2.3 How It Works

    This section explores how Spring Cloud Contract Verifier with Stub Runner works.

    2.3.1 A Three-second Tour

    This very brief tour walks through using Spring Cloud Contract:

    You can find a somewhat longer tour +here.

    On the Producer Side

    To start working with Spring Cloud Contract, add files with REST/ messaging contracts +expressed in either Groovy DSL or YAML to the contracts directory, which is set by the +contractsDslDir property. By default, it is $rootDir/src/test/resources/contracts.

    Then add the Spring Cloud Contract Verifier dependency and plugin to your build file, as +shown in the following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
    +	<scope>test</scope>
    +</dependency>

    The following listing shows how to add the plugin, which should go in the build/plugins +portion of the file:

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +</plugin>

    Running ./mvnw clean install automatically generates tests that verify the application +compliance with the added contracts. By default, the tests get generated under +org.springframework.cloud.contract.verifier.tests..

    As the implementation of the functionalities described by the contracts is not yet +present, the tests fail.

    To make them pass, you must add the correct implementation of either handling HTTP +requests or messages. Also, you must add a correct base test class for auto-generated +tests to the project. This class is extended by all the auto-generated tests, and it +should contain all the setup necessary to run them (for example RestAssuredMockMvc +controller setup or messaging test setup).

    Once the implementation and the test base class are in place, the tests pass, and both the +application and the stub artifacts are built and installed in the local Maven repository. +The changes can now be merged, and both the application and the stub artifacts may be +published in an online repository.

    On the Consumer Side

    Spring Cloud Contract Stub Runner can be used in the integration tests to get a running +WireMock instance or messaging route that simulates the actual service.

    To do so, add the dependency to Spring Cloud Contract Stub Runner, as shown in the +following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    +	<scope>test</scope>
    +</dependency>

    You can get the Producer-side stubs installed in your Maven repository in either of two +ways:

    • By checking out the Producer side repository and adding contracts and generating the stubs +by running the following commands:

      $ cd local-http-server-repo
      +$ ./mvnw clean install -DskipTests
      [Tip]Tip

      The tests are being skipped because the Producer-side contract implementation is not +in place yet, so the automatically-generated contract tests fail.

    • By getting already-existing producer service stubs from a remote repository. To do so, +pass the stub artifact IDs and artifact repository URL as Spring Cloud Contract +Stub Runner properties, as shown in the following example:

      stubrunner:
      +  ids: 'com.example:http-server-dsl:+:stubs:8080'
      +  repositoryRoot: http://repo.spring.io/libs-snapshot

    Now you can annotate your test class with @AutoConfigureStubRunner. In the annotation, +provide the group-id and artifact-id values for Spring Cloud Contract Stub Runner to +run the collaborators' stubs for you, as shown in the following example:

    @RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment=WebEnvironment.NONE)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    +@DirtiesContext
    +public class LoanApplicationServiceTests {
    [Tip]Tip

    Use the REMOTE stubsMode when downloading stubs from an online repository and +LOCAL for offline work.

    Now, in your integration test, you can receive stubbed versions of HTTP responses or +messages that are expected to be emitted by the collaborator service.

    2.3.2 A Three-minute Tour

    This brief tour walks through using Spring Cloud Contract:

    You can find an even more brief tour +here.

    On the Producer Side

    To start working with Spring Cloud Contract, add files with REST/ messaging contracts +expressed in either Groovy DSL or YAML to the contracts directory, which is set by the +contractsDslDir property. By default, it is $rootDir/src/test/resources/contracts.

    For the HTTP stubs, a contract defines what kind of response should be returned for a +given request (taking into account the HTTP methods, URLs, headers, status codes, and so +on). The following example shows how an HTTP stub contract in Groovy DSL:

    package contracts
    +
    +org.springframework.cloud.contract.spec.Contract.make {
    +	request {
    +		method 'PUT'
    +		url '/fraudcheck'
    +		body([
    +			   "client.id": $(regex('[0-9]{10}')),
    +			   loanAmount: 99999
    +		])
    +		headers {
    +			contentType('application/json')
    +		}
    +	}
    +	response {
    +		status OK()
    +		body([
    +			   fraudCheckStatus: "FRAUD",
    +			   "rejection.reason": "Amount too high"
    +		])
    +		headers {
    +			contentType('application/json')
    +		}
    +	}
    +}

    The same contract expressed in YAML would look like the following example:

    request:
    +  method: PUT
    +  url: /fraudcheck
    +  body:
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers:
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id']
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response:
    +  status: 200
    +  body:
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers:
    +    Content-Type: application/json;charset=UTF-8

    In the case of messaging, you can define:

    • The input and the output messages can be defined (taking into account from and where it +was sent, the message body, and the header).
    • The methods that should be called after the message is received.
    • The methods that, when called, should trigger a message.

    The following example shows a Camel messaging contract expressed in Groovy DSL:

    def contractDsl = Contract.make {
    +	label 'some_label'
    +	input {
    +		messageFrom('jms:delete')
    +		messageBody([
    +				bookName: 'foo'
    +		])
    +		messageHeaders {
    +			header('sample', 'header')
    +		}
    +		assertThat('bookWasDeleted()')
    +	}
    +}

    The following example shows the same contract expressed in YAML:

    label: some_label
    +input:
    +  messageFrom: jms:delete
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +  assertThat: bookWasDeleted()

    Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file, +as shown in the following example:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
    +	<scope>test</scope>
    +</dependency>

    The following listing shows how to add the plugin, which should go in the build/plugins +portion of the file:

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +</plugin>

    Running ./mvnw clean install automatically generates tests that verify the application +compliance with the added contracts. By default, the generated tests are under +org.springframework.cloud.contract.verifier.tests..

    The following example shows a sample auto-generated test for an HTTP contract:

    @Test
    +public void validate_shouldMarkClientAsFraud() throws Exception {
    +    // given:
    +        MockMvcRequestSpecification request = given()
    +                .header("Content-Type", "application/vnd.fraud.v1+json")
    +                .body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
    +
    +    // when:
    +        ResponseOptions response = given().spec(request)
    +                .put("/fraudcheck");
    +
    +    // then:
    +        assertThat(response.statusCode()).isEqualTo(200);
    +        assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
    +    // and:
    +        DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
    +        assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
    +        assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
    +}

    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 +used. (To do so, change the testMode property of the plugin to JAX-RS or EXPLICIT, +respectively.)

    Apart from the default JUnit, you can instead use Spock tests, by setting the plugin +testFramework property to Spock.

    [Tip]Tip

    You can now also generate WireMock scenarios based on the contracts, by including an +order number followed by an underscore at the beginning of the contract file names.

    The following example shows an auto-generated test in Spock for a messaging stub contract:

    [source,groovy,indent=0]
    given:
    +	 ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
    +		\'\'\'{"bookName":"foo"}\'\'\',
    +		['sample': 'header']
    +	)
    +
    +when:
    +	 contractVerifierMessaging.send(inputMessage, 'jms:delete')
    +
    +then:
    +	 noExceptionThrown()
    +	 bookWasDeleted()

    As the implementation of the functionalities described by the contracts is not yet +present, the tests fail.

    To make them pass, you must add the correct implementation of handling either HTTP +requests or messages. Also, you must add a correct base test class for auto-generated +tests to the project. This class is extended by all the auto-generated tests and should +contain all the setup necessary to run them (for example, RestAssuredMockMvc controller +setup or messaging test setup).

    Once the implementation and the test base class are in place, the tests pass, and both the +application and the stub artifacts are built and installed in the local Maven repository. +Information about installing the stubs jar to the local repository appears in the logs, as +shown in the following example:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
    +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
    +[INFO]
    +[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
    +[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
    +[INFO]
    +[INFO] --- spring-boot-maven-plugin:1.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
    +[INFO]
    +[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
    +[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar
    +[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom
    +[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

    You can now merge the changes and publish both the application and the stub artifacts +in an online repository.

    Docker Project

    In order to enable working with contracts while creating applications in non-JVM +technologies, the springcloud/spring-cloud-contract Docker image has been created. It +contains a project that automatically generates tests for HTTP contracts and executes them +in EXPLICIT test mode. Then, if the tests pass, it generates Wiremock stubs and, +optionally, publishes them to an artifact manager. In order to use the image, you can +mount the contracts into the /contracts directory and set a few environment variables.

    On the Consumer Side

    Spring Cloud Contract Stub Runner can be used in the integration tests to get a running +WireMock instance or messaging route that simulates the actual service.

    To get started, add the dependency to Spring Cloud Contract Stub Runner:

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    +	<scope>test</scope>
    +</dependency>

    You can get the Producer-side stubs installed in your Maven repository in either of two +ways:

    • By checking out the Producer side repository and adding contracts and generating the +stubs by running the following commands:

      $ cd local-http-server-repo
      +$ ./mvnw clean install -DskipTests
      [Note]Note

      The tests are skipped because the Producer-side contract implementation is not yet +in place, so the automatically-generated contract tests fail.

    • Getting already existing producer service stubs from a remote repository. To do so, +pass the stub artifact IDs and artifact repository URl as Spring Cloud Contract Stub +Runner properties, as shown in the following example:

      stubrunner:
      +  ids: 'com.example:http-server-dsl:+:stubs:8080'
      +  repositoryRoot: http://repo.spring.io/libs-snapshot

    Now you can annotate your test class with @AutoConfigureStubRunner. In the annotation, +provide the group-id and artifact-id for Spring Cloud Contract Stub Runner to run +the collaborators' stubs for you, as shown in the following example:

    @RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment=WebEnvironment.NONE)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
    +@DirtiesContext
    +public class LoanApplicationServiceTests {
    [Tip]Tip

    Use the REMOTE stubsMode when downloading stubs from an online repository and +LOCAL for offline work.

    In your integration test, you can receive stubbed versions of HTTP responses or messages +that are expected to be emitted by the collaborator service. You can see entries similar +to the following in the build logs:

    2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
    +2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
    +2016-07-19 14:22:25.439  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
    +2016-07-19 14:22:25.451  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
    +2016-07-19 14:22:25.465  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
    +2016-07-19 14:22:25.475  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
    +2016-07-19 14:22:27.737  INFO 41050 --- [           main] o.s.c.c.stubrunner.StubRunnerExecutor    : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]

    2.3.3 Defining the Contract

    As consumers of services, we need to define what exactly we want to achieve. We need to formulate our expectations. That is why we write contracts.

    Assume that you want to send a request containing the ID of a client company and the amount it wants to borrow from us. You also want to send it to the /fraudcheck url via -the PUT method.

    package contracts
    +the PUT method.

    Groovy DSL.  +

    package contracts
     
     org.springframework.cloud.contract.spec.Contract.make {
     	request { // (1)
    @@ -47,7 +264,7 @@ org.springframework.cloud.contract.spec.Contract.make {
     		}
     	}
     	response { // (6)
    -		status 200 // (7)
    +		status OK() // (7)
     		body([ // (8)
     			   fraudCheckStatus: "FRAUD",
     			   "rejection.reason": "Amount too high"
    @@ -65,7 +282,7 @@ From the Consumer perspective, when shooting a request in the integration test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the response will be sent with
    @@ -80,7 +297,7 @@ From the Producer perspective, in the autogenerated producer-side test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the test will assert if the response has been sent with
    @@ -88,21 +305,76 @@ From the Producer perspective, in the autogenerated producer-side test:
     (8) - and JSON body equal to
      { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
     (9) - with header `Content-Type` matching `application/json.*`
    - */

    2.3.2 Client Side

    Spring Cloud Contract generates stubs, which you can use during client-side testing. + */

    +

    YAML.  +

    request: # (1)
    +  method: PUT # (2)
    +  url: /fraudcheck # (3)
    +  body: # (4)
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers: # (5)
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id'] # (6)
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response: # (7)
    +  status: 200 # (8)
    +  body:  # (9)
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers: # (10)
    +    Content-Type: application/json;charset=UTF-8
    +
    +
    +#From the Consumer perspective, when shooting a request in the integration test:
    +#
    +#(1) - If the consumer sends a request
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
    +#(7) - then the response will be sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json`
    +#
    +#From the Producer perspective, in the autogenerated producer-side test:
    +#
    +#(1) - A request will be sent to the producer
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id` `1234567890`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(7) - then the test will assert if the response has been sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

    +

    2.3.4 Client Side

    Spring Cloud Contract generates stubs, which you can use during client-side testing. You get a running WireMock instance/Messaging route that simulates the service. You would like to feed that instance with a proper stub definition.

    At some point in time, you need to send a request to the Fraud Detection service.

    ResponseEntity<FraudServiceResponse> response =
     		restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
     				new HttpEntity<>(request, httpHeaders),
     				FraudServiceResponse.class);

    Annotate your test class with @AutoConfigureStubRunner. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment=WebEnvironment.NONE)
    -@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
     @DirtiesContext
     public class LoanApplicationServiceTests {

    After that, during the tests, Spring Cloud Contract automatically finds the stubs (simulating the real service) in the Maven repository and exposes them on a configured -(or random) port.

    2.3.3 Server Side

    Since you are developing your stub, you need to be sure that it actually resembles your +(or random) port.

    2.3.5 Server Side

    Since you are developing your stub, you need to be sure that it actually resembles your concrete implementation. You cannot have a situation where your stub acts in one way and your application behaves in a different way, especially in production.

    To ensure that your application behaves the way you define in your stub, tests are -generated from the stub you provide.

    The autogenerated test looks like this:

    @Test
    +generated from the stub you provide.

    The autogenerated test looks, more or less, like this:

    @Test
     public void validate_shouldMarkClientAsFraud() throws Exception {
         // given:
             MockMvcRequestSpecification request = given()
    @@ -126,8 +398,8 @@ us. The current implementation of our system grants loans to everybody.

    As sprint, we must develop a new feature: if a client wants to borrow too much money, then we mark the client as a fraud.

    Technical remark - Fraud Detection has an artifact-id of http-server, while Loan Issuance has an artifact-id of http-client, and both have a group-id of com.example.

    Social remark - both client and server development teams need to communicate directly and -discuss changes while going through the process. CDC is all about communication.

    The server -side code is available here and the +discuss changes while going through the process. CDC is all about communication.

    The server +side code is available here and the client code here.

    [Tip]Tip

    In this case, the producer owns the contracts. Physically, all the contract are in the producer’s repository.

    2.4.1 Technical note

    If using the SNAPSHOT / Milestone / Release Candidate versions please add the following section to your build:

    Maven.  @@ -211,9 +483,10 @@ client wants to borrow. You want to send it to the /fraudc FraudServiceResponse.class);

    For simplicity, the port of the Fraud Detection service is set to 8080, and the application runs on 8090.

    If you start the test at this point, it breaks, because no service currently runs on port 8080.

    Clone the Fraud Detection service repository locally.

    You can start by playing around with the server side contract. To do so, you must first -clone it.

    git clone https://your-git-server.com/server-side.git local-http-server-repo

    Define the contract locally in the repo of Fraud Detection service.

    As a consumer, you need to define what exactly you want to achieve. You need to formulate +clone it.

    $ git clone https://your-git-server.com/server-side.git local-http-server-repo

    Define the contract locally in the repo of Fraud Detection service.

    As a consumer, you need to define what exactly you want to achieve. You need to formulate your expectations. To do so, write the following contract:

    [Important]Important

    Place the contract under src/test/resources/contracts/fraud folder. The fraud folder -is important because the producer’s test base class name references that folder.

    package contracts
    +is important because the producer’s test base class name references that folder.

    Groovy DSL.  +

    package contracts
     
     org.springframework.cloud.contract.spec.Contract.make {
     	request { // (1)
    @@ -228,7 +501,7 @@ org.springframework.cloud.contract.spec.Contract.make {
     		}
     	}
     	response { // (6)
    -		status 200 // (7)
    +		status OK() // (7)
     		body([ // (8)
     			   fraudCheckStatus: "FRAUD",
     			   "rejection.reason": "Amount too high"
    @@ -246,7 +519,7 @@ From the Consumer perspective, when shooting a request in the integration test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the response will be sent with
    @@ -261,7 +534,7 @@ From the Producer perspective, in the autogenerated producer-side test:
     (2) - With the "PUT" method
     (3) - to the URL "/fraudcheck"
     (4) - with the JSON body that
    - * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
    + * has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
      * has a field `loanAmount` that is equal to `99999`
     (5) - with header `Content-Type` equal to `application/json`
     (6) - then the test will assert if the response has been sent with
    @@ -269,15 +542,69 @@ From the Producer perspective, in the autogenerated producer-side test:
     (8) - and JSON body equal to
      { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
     (9) - with header `Content-Type` matching `application/json.*`
    - */

    The Contract is written using a statically typed Groovy DSL. You might wonder what about -those value(client(…​), server(…​)) parts. By using this notation, Spring Cloud + */

    +

    YAML.  +

    request: # (1)
    +  method: PUT # (2)
    +  url: /fraudcheck # (3)
    +  body: # (4)
    +    "client.id": 1234567890
    +    loanAmount: 99999
    +  headers: # (5)
    +    Content-Type: application/json
    +  matchers:
    +    body:
    +      - path: $.['client.id'] # (6)
    +        type: by_regex
    +        value: "[0-9]{10}"
    +response: # (7)
    +  status: 200 # (8)
    +  body:  # (9)
    +    fraudCheckStatus: "FRAUD"
    +    "rejection.reason": "Amount too high"
    +  headers: # (10)
    +    Content-Type: application/json;charset=UTF-8
    +
    +
    +#From the Consumer perspective, when shooting a request in the integration test:
    +#
    +#(1) - If the consumer sends a request
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
    +#(7) - then the response will be sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json`
    +#
    +#From the Producer perspective, in the autogenerated producer-side test:
    +#
    +#(1) - A request will be sent to the producer
    +#(2) - With the "PUT" method
    +#(3) - to the URL "/fraudcheck"
    +#(4) - with the JSON body that
    +# * has a field `client.id` `1234567890`
    +# * has a field `loanAmount` that is equal to `99999`
    +#(5) - with header `Content-Type` equal to `application/json`
    +#(7) - then the test will assert if the response has been sent with
    +#(8) - status equal `200`
    +#(9) - and JSON body equal to
    +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
    +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

    +

    The YML contract is quite straight-forward. However when you take a look at the Contract +written using a statically typed Groovy DSL - you might wonder what the +value(client(…​), server(…​)) parts are. By using this notation, Spring Cloud Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case of an identifier or a timestamp, you need not hardcode a value. You want to allow some different ranges of values. To enable ranges of values, you can set regular expressions matching those values for the consumer side. You can provide the body by means of either a map notation or String with interpolations. -Consult the docs -for more information. We highly recommend using the map notation!

    [Tip]Tip

    You must understand the map notation in order to set up contracts. Please read the +Consult the ??? section for more information. We highly recommend using the map notation!

    [Tip]Tip

    You must understand the map notation in order to set up contracts. Please read the Groovy docs regarding JSON.

    The previously shown contract is an agreement between two sides that:

    • if an HTTP request is sent with all of

      • a PUT method on the /fraudcheck endpoint,
      • a JSON body with a client.id that matches the regular expression [0-9]{10} and loanAmount equal to 99999,
      • and a Content-Type header with a value of application/vnd.fraud.v1+json,
    • then an HTTP response is sent to the consumer that

      • has status 200,
      • contains a JSON body with the fraudCheckStatus field containing a value FRAUD and the rejectionReason field having value Amount too high,
      • and a Content-Type header with a value of application/vnd.fraud.v1+json.

    Once you are ready to check the API in practice in the integration tests, you need to @@ -302,8 +629,8 @@ First, add the Spring Cloud Contract BOM.

    </configuration>
     </plugin>

    Since the plugin was added, you get the Spring Cloud Contract Verifier features which, from the provided contracts:

    • generate and run tests
    • produce and install stubs

    You do not want to generate tests since you, as the consumer, want only to play with the -stubs. You need to skip the test generation and execution. When you execute:

    cd local-http-server-repo
    -./mvnw clean install -DskipTests

    In the logs, you see something like this:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
    +stubs. You need to skip the test generation and execution. When you execute:

    $ cd local-http-server-repo
    +$ ./mvnw clean install -DskipTests

    In the logs, you see something like this:

    [INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
     [INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
     [INFO]
     [INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
    @@ -334,9 +661,10 @@ Application service):

    Add the Spring Cloud Co </dependency>

    Annotate your test class with @AutoConfigureStubRunner. In the annotation, provide the group-id and artifact-id for the Stub Runner to download the stubs of your collaborators. (Optional step) Because you’re playing with the collaborators offline, you -can also provide the offline work switch.

    @RunWith(SpringRunner.class)
    +can also provide the offline work switch (StubRunnerProperties.StubsMode.LOCAL).

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment=WebEnvironment.NONE)
    -@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
    +@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
    +		stubsMode = StubRunnerProperties.StubsMode.LOCAL)
     @DirtiesContext
     public class LoanApplicationServiceTests {

    Now, when you run your tests, you see something like this:

    2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
     2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
    @@ -352,8 +680,8 @@ you wish.

    Once you are satisfied with the results and the test passes, pub the server side. Currently, the consumer side work is done.

    2.4.3 Producer side (Fraud Detection server)

    As a developer of the Fraud Detection server (a server to the Loan Issuance service):

    Create an initial implementation.

    As a reminder, you can see the initial implementation here:

    @RequestMapping(value = "/fraudcheck", method = PUT)
     public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
     return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
    -}

    Take over the pull request.

    git checkout -b contract-change-pr master
    -git pull https://your-git-server.com/server-side-fork.git contract-change-pr

    You must add the dependencies needed by the autogenerated tests:

    <dependency>
    +}

    Take over the pull request.

    $ git checkout -b contract-change-pr master
    +$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr

    You must add the dependencies needed by the autogenerated tests:

    <dependency>
     	<groupId>org.springframework.cloud</groupId>
     	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
     	<scope>test</scope>
    @@ -423,8 +751,9 @@ like this:

    "['fraudCheckStatus']").matches("[A-Z]{5}");
             assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
    -}

    As you can see, all the producer() parts of the Contract that were present in the -value(consumer(…​), producer(…​)) blocks got injected into the test.

    Note that, on the producer side, you are also doing TDD. The expectations are expressed +}

    If you used the Groovy DSL, you can see, all the producer() parts of the Contract that were present in the +value(consumer(…​), producer(…​)) blocks got injected into the test. +In case of using YAML, the same applied for the matchers sections of the response.

    Note that, on the producer side, you are also doing TDD. The expectations are expressed in the form of a test. This test sends a request to our own application with the URL, headers, and body defined in the contract. It also is expecting precisely defined values in the response. In other words, you have the red part of red, green, and @@ -437,23 +766,23 @@ implementation:

    return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
     }

    When you execute ./mvnw clean install again, the tests pass. Since the Spring Cloud Contract Verifier plugin adds the tests to the generated-test-sources, you can -actually run those tests from your IDE.

    Deploy your app.

    Once you finish your work, you can deploy your change. First, merge the branch:

    git checkout master
    -git merge --no-ff contract-change-pr
    -git push origin master

    Your CI might run something like ./mvnw clean deploy, which would publish both the -application and the stub artifacts.

    2.4.4 Consumer Side (Loan Issuance) Final Step

    As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):

    Merge branch to master.

    git checkout master
    -git merge --no-ff contract-change-pr

    Work online.

    Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate +actually run those tests from your IDE.

    Deploy your app.

    Once you finish your work, you can deploy your change. First, merge the branch:

    $ git checkout master
    +$ git merge --no-ff contract-change-pr
    +$ git push origin master

    Your CI might run something like ./mvnw clean deploy, which would publish both the +application and the stub artifacts.

    2.4.4 Consumer Side (Loan Issuance) Final Step

    As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):

    Merge branch to master.

    $ git checkout master
    +$ git merge --no-ff contract-change-pr

    Work online.

    Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate where the repository with your stubs is located. At this moment the stubs of the server -side are automatically downloaded from Nexus/Artifactory. You can switch off the value of -the workOffline parameter in your annotation. The following code shows an example of +side are automatically downloaded from Nexus/Artifactory. You can set the value of +stubsMode to REMOTE. The following code shows an example of achieving the same thing by changing the properties.

    stubrunner:
       ids: 'com.example:http-server-dsl:+:stubs:8080'
       repositoryRoot: http://repo.spring.io/libs-snapshot

    That’s it!

    2.5 Dependencies

    The best way to add dependencies is to use the proper starter dependency.

    For stub-runner, use spring-cloud-starter-stub-runner. When you use a plugin, add spring-cloud-starter-contract-verifier.

    2.6 Additional Links

    Here are some resources related to Spring Cloud Contract Verifier and Stub Runner. Note that some may be outdated, because the Spring Cloud Contract Verifier project is under constant development.

    2.6.1 Spring Cloud Contract video

    You can check out the video from the Warsaw JUG about Spring Cloud Contract:

    2.7 Samples

    You can find some samples at -samples.

    3. Spring Cloud Contract FAQ

    3.1 Why use Spring Cloud Contract Verifier and not X ?

    For the time being Spring Cloud Contract Verifier is a JVM based tool. So it could be your first pick when you’re already creating +samples.

    3. Spring Cloud Contract FAQ

    3.1 Why use Spring Cloud Contract Verifier and not X ?

    For the time being Spring Cloud Contract is a JVM based tool. So it could be your first pick when you’re already creating software for the JVM. This project has a lot of really interesting features but especially quite a few of them definitely make -Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:

    • Possibility to do CDC with messaging
    • Clear and easy to use, statically typed DSL
    • Possibility to copy paste your current JSON file to the contract and only edit its elements
    • Automatic generation of tests from the defined Contract
    • Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory
    • Spring Cloud integration - no discovery service is needed for integration tests

    3.2 What is this value(consumer(), producer()) ?

    One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose. +Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:

    • Possibility to do CDC with messaging
    • Clear and easy to use, statically typed DSL
    • Possibility to copy paste your current JSON file to the contract and only edit its elements
    • Automatic generation of tests from the defined Contract
    • Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory
    • Spring Cloud integration - no discovery service is needed for integration tests
    • Spring Cloud Contract integrates with Pact out of the box and provides easy hooks to extend its functionality
    • Via Docker adds support for any language & framework used

    3.2 I don’t want to write a contract in Groovy!

    No problem. You can write a contract in YAML!

    3.3 What is this value(consumer(), producer()) ?

    One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose. What typically makes that difficult are the hard-coded values of request / response elements. For example dates or ids. Imagine the following JSON request

    {
         "time" : "2016-10-10 20:10:15",
    @@ -496,7 +825,7 @@ sides of the communication. You can pass the values:

    Either via the

    or using the $() method

    $(consumer(...), producer(...))
     $(stub(...), test(...))
    -$(client(...), server(...))

    You can read more about this in the Contract DSL section.

    Calling value() or $() tells Spring Cloud Contract that you will be passing a dynamic value. +$(client(...), server(...))

    You can read more about this in the ??? section.

    Calling value() or $() tells Spring Cloud Contract that you will be passing a dynamic value. Inside the consumer() method you pass the value that should be used on the consumer side (in the generated stub). Inside the producer() method you pass the value that should be used on the producer side (in the generated test).

    [Tip]Tip

    If on one side you have passed the regular expression and you haven’t passed the other, then the other side will get auto-generated.

    Most often you will use that method together with the regex helper method. E.g. consumer(regex('[0-9]{10}')).

    To sum it up the contract for the aforementioned scenario would look more or less like this (the regular expression @@ -511,7 +840,7 @@ for time and UUID are simplified and most likely invalid but we want to keep thi ]) } response { - status 200 + status OK() body([ time : value(producer(regex('[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]')), id: value([producer(regex('[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}')) @@ -519,21 +848,21 @@ for time and UUID are simplified and most likely invalid but we want to keep thi ]) } }

    [Important]Important

    Please read the Groovy docs related to JSON to understand how to -properly structure the request / response bodies.

    3.3 How to do Stubs versioning?

    3.3.1 API Versioning

    Let’s try to answer a question what versioning really means. If you’re referring to the API version then there are +properly structure the request / response bodies.

    3.4 How to do Stubs versioning?

    3.4.1 API Versioning

    Let’s try to answer a question what versioning really means. If you’re referring to the API version then there are different approaches.

    • use Hypermedia, links and do not version your API by any means
    • pass versions through headers / urls

    I will not try to answer a question which approach is better. Whatever suit your needs and allows you to generate business value should be picked.

    Let’s assume that you do version your API. In that case you should provide as many contracts as many versions you support. -You can create a subfolder for every version or append it to th contract name - whatever suits you more.

    3.3.2 JAR versioning

    If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.

    Let’s assume that you’re doing Continuous Delivery / Deployment which means that you’re generating a new version of +You can create a subfolder for every version or append it to th contract name - whatever suits you more.

    3.4.2 JAR versioning

    If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.

    Let’s assume that you’re doing Continuous Delivery / Deployment which means that you’re generating a new version of the jar each time you go through the pipeline and that jar can go to production at any time. For example your jar version looks like this (it got built on the 20.10.2016 at 20:15:21) :

    1.0.0.20161020-201521-RELEASE

    In that case your generated stub jar will look like this.

    1.0.0.20161020-201521-RELEASE-stubs.jar

    In this case you should inside your application.yml or @AutoConfigureStubRunner when referencing stubs provide the latest version of the stubs. You can do that by passing the + sign. Example

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

    If the versioning however is fixed (e.g. 1.0.4.RELEASE or 2.1.1) then you have to set the concrete value of the jar -version. Example for 2.1.1.

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})

    3.3.3 Dev or prod stubs

    You can manipulate the classifier to run the tests against current development version of the stubs of other services +version. Example for 2.1.1.

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})

    3.4.3 Dev or prod stubs

    You can manipulate the classifier to run the tests against current development version of the stubs of other services or the ones that were deployed to production. If you alter your build to deploy the stubs with the prod-stubs classifier - once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.

    Example of tests using development version of stubs

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

    Example of tests using production version of stubs

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})

    You can pass those values also via properties from your deployment pipeline.

    3.4 Common repo with contracts

    Another way of storing contracts other than having them with the producer is keeping them in a common place. + once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.

    Example of tests using development version of stubs

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

    Example of tests using production version of stubs

    @AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})

    You can pass those values also via properties from your deployment pipeline.

    3.5 Common repo with contracts

    Another way of storing contracts other than having them with the producer is keeping them in a common place. It can be related to security issues where the consumers can’t clone the producer’s code. Also if you keep contracts in a single place then you, as a producer, will know how many consumers you have and which -consumer will you break with your local changes.

    3.4.1 Repo structure

    Let’s assume that we have a producer with coordinates com.example:server and 3 consumers: client1, +consumer will you break with your local changes.

    3.5.1 Repo structure

    Let’s assume that we have a producer with coordinates com.example:server and 3 consumers: client1, client2, client3. Then in the repository with common contracts you would have the following setup -(which you can checkout here:

    ├── com
    +(which you can checkout here):

    ├── com
     │   └── example
     │       └── server
     │           ├── client1
    @@ -566,15 +895,15 @@ one to one to the contents of the repo.

    Example of a <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> - <version>1.5.8.RELEASE</version> + <version>2.0.3.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> - <spring-cloud-contract.version>1.2.2.BUILD-SNAPSHOT</spring-cloud-contract.version> - <spring-cloud-dependencies.version>Edgware.BUILD-SNAPSHOT</spring-cloud-dependencies.version> + <spring-cloud-contract.version>2.1.0.BUILD-SNAPSHOT</spring-cloud-contract.version> + <spring-cloud-dependencies.version>Finchley.BUILD-SNAPSHOT</spring-cloud-dependencies.version> <excludeBuildFolders>true</excludeBuildFolders> </properties> @@ -722,15 +1051,16 @@ Those poms are necessary for the consumer side to run mvn </excludes> </fileSet> </fileSets> -</assembly>

    3.4.2 Workflow

    The workflow would look similar to the one presented in the Step by step guide to CDC. The only difference +</assembly>

    3.5.2 Workflow

    The workflow would look similar to the one presented in the Step by step guide to CDC. The only difference is that the producer doesn’t own the contracts anymore. So the consumer and the producer have to work on - common contracts in a common repository.

    3.4.3 Consumer

    When the consumer wants to work on the contracts offline, instead of cloning the producer code, the + common contracts in a common repository.

    3.5.3 Consumer

    When the consumer wants to work on the contracts offline, instead of cloning the producer code, the consumer team clones the common repository, goes to the required producer’s folder (e.g. com/example/server) -and runs mvn clean install -DskipTests to install locally the stubs converted from the contracts.

    [Tip]Tip

    You need to have Maven installed locally

    3.4.4 Producer

    As a producer it’s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency +and runs mvn clean install -DskipTests to install locally the stubs converted from the contracts.

    [Tip]Tip

    You need to have Maven installed locally

    3.5.4 Producer

    As a producer it’s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency of the JAR containing the contracts:

    <plugin>
     	<groupId>org.springframework.cloud</groupId>
     	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
     	<configuration>
    +		<contractsMode>REMOTE</contractsMode>
     		<contractsRepositoryUrl>http://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
     		<contractDependency>
     			<groupId>com.example.standalone</groupId>
    @@ -741,16 +1071,358 @@ of the JAR containing the contracts:

    http://link/to/your/nexus/or/artifactory/or/sth. It will be then unpacked in a local temporary folder
     and contracts present under the com/example/server will be picked as the ones used to generate the
     tests and the stubs. Due to this convention the producer team will know which consumer teams will be broken
    -when some incompatible changes are done.

    The rest of the flow looks the same.

    3.5 Can I have multiple base classes for tests?

    Yes! Check out the Different base classes for contracts sections -of either Gradle or Maven plugins.

    3.6 How can I debug the request/response being sent by the generated tests client?

    The generated tests all boil down to RestAssured in some form or fashion which relies on Apache HttpClient. HttpClient has a facility called wire logging which logs the entire request and response to HttpClient. Spring Boot has a logging common application property for doing this sort of thing, just add this to your application properties

    logging.level.org.apache.http.wire=DEBUG

    3.6.1 How can I debug the mapping/request/response being sent by WireMock?

    Starting from version 1.2.0 we turn on WireMock logging to +when some incompatible changes are done.

    The rest of the flow looks the same.

    3.5.5 How can I define messaging contracts per topic not per producer?

    To avoid messaging contracts duplication in the common repo, when few producers writing messages to one topic, +we could create the structure when the rest contracts would be placed in a folder per producer and messaging +contracts in the folder per topic.

    For Maven Project

    To make it possible to work on the producer side we could do the following things (all via Maven plugins):

    • Add common repo dependency to your classpath:
    <dependency>
    +   <groupId>com.example</groupId>
    +   <artifactId>common-repo</artifactId>
    +   <version>${common-repo.version}</version>
    +</dependency>
    • Download the JAR with the contracts and unpack the JAR to target:
    <plugin>
    +   <groupId>org.apache.maven.plugins</groupId>
    +   <artifactId>maven-dependency-plugin</artifactId>
    +   <version>3.0.0</version>
    +   <executions>
    +      <execution>
    +         <id>unpack-dependencies</id>
    +         <phase>process-resources</phase>
    +         <goals>
    +            <goal>unpack</goal>
    +         </goals>
    +         <configuration>
    +            <artifactItems>
    +               <artifactItem>
    +                  <groupId>com.example</groupId>
    +                  <artifactId>common-repo</artifactId>
    +                  <type>jar</type>
    +                  <overWrite>false</overWrite>
    +                  <outputDirectory>${project.build.directory}/contracts</outputDirectory>
    +               </artifactItem>
    +            </artifactItems>
    +         </configuration>
    +      </execution>
    +   </executions>
    +</plugin>
    • Rip out all the folders we’re not interested in:
    <plugin>
    +   <groupId>org.apache.maven.plugins</groupId>
    +   <artifactId>maven-antrun-plugin</artifactId>
    +   <version>1.8</version>
    +   <executions>
    +      <execution>
    +         <phase>process-resources</phase>
    +         <goals>
    +            <goal>run</goal>
    +         </goals>
    +         <configuration>
    +            <tasks>
    +               <delete includeemptydirs="true">
    +                  <fileset dir="${project.build.directory}/contracts">
    +                     <include name="**/*" />
    +                     <!--Producer artifactId-->
    +                     <exclude name="**/${project.artifactId}/**" />
    +                     <!--List of the supported topics-->
    +                     <exclude name="**/${first-topic}/**" />
    +                     <exclude name="**/${second-topic}/**" />
    +                  </fileset>
    +               </delete>
    +            </tasks>
    +         </configuration>
    +      </execution>
    +   </executions>
    +</plugin>
    • Run the contract plugin by pointing to the contracts to the folder under target:
    <plugin>
    +   <groupId>org.springframework.cloud</groupId>
    +   <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +   <version>${spring-cloud-contract.version}</version>
    +   <extensions>true</extensions>
    +   <configuration>
    +      <packageWithBaseClasses>com.example</packageWithBaseClasses>
    +      <baseClassMappings>
    +         <baseClassMapping>
    +            <contractPackageRegex>.*intoxication.*</contractPackageRegex>
    +            <baseClassFQN>com.example.intoxication.BeerIntoxicationBase</baseClassFQN>
    +         </baseClassMapping>
    +      </baseClassMappings>
    +      <contractsDirectory>${project.build.directory}/contracts</contractsDirectory>
    +   </configuration>
    +</plugin>

    For Gradle Project

    • Add a custom configuration for the common-repo dependency:
    ext {
    +    conractsGroupId = "com.example"
    +    contractsArtifactId = "common-repo"
    +    contractsVersion = "1.2.3"
    +}
    +
    +configurations {
    +    contracts {
    +        transitive = false
    +    }
    +}
    • Add the common-repo dependency to your classpath:
    dependencies {
    +    contracts "${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"
    +    testCompile "${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"
    +}
    • Download the dependency to an appropriate folder:
    task getContracts(type: Copy) {
    +    from configurations.contracts
    +    into new File(project.buildDir, "downloadedContracts")
    +}
    • Unzip JAR:
    task unzipContracts(type: Copy) {
    +    def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar")
    +    def outputDir = file("${buildDir}/unpackedContracts")
    +
    +    from zipTree(zipFile)
    +    into outputDir
    +}
    • Cleanup unused contracts:
    task deleteUnwantedContracts(type: Delete) {
    +    delete fileTree(dir: "${buildDir}/unpackedContracts",
    +        include: "**/*",
    +        excludes: [
    +            "**/${project.name}/**"",
    +            "**/${first-topic}/**",
    +            "**/${second-topic}/**"])
    +}
    • Create task dependencies:
    unzipContracts.dependsOn("getContracts")
    +deleteUnwantedContracts.dependsOn("unzipContracts")
    +build.dependsOn("deleteUnwantedContracts")
    • Configure plugin by specifying the directory containing contracts using contractsDslDir property
    contracts {
    +    contractsDslDir = new File("${buildDir}/unpackedContracts")
    +}

    3.6 Do I need a Binary Storage? Can’t I use Git?

    In the polyglot world, there are languages that don’t use binary storages like +Artifactory or Nexus. Starting from Spring Cloud Contract version 2.0.0 we provide +mechanisms to store contracts and stubs in a SCM repository. Currently the +only supported SCM is Git.

    The repository would have to the following setup +(which you can checkout here):

    .
    +└── META-INF
    +    └── com.example
    +        └── beer-api-producer-git
    +            └── 0.0.1-SNAPSHOT
    +                ├── contracts
    +                │   └── beer-api-consumer
    +                │       ├── messaging
    +                │       │   ├── shouldSendAcceptedVerification.groovy
    +                │       │   └── shouldSendRejectedVerification.groovy
    +                │       └── rest
    +                │           ├── shouldGrantABeerIfOldEnough.groovy
    +                │           └── shouldRejectABeerIfTooYoung.groovy
    +                └── mappings
    +                    └── beer-api-consumer
    +                        └── rest
    +                            ├── shouldGrantABeerIfOldEnough.json
    +                            └── shouldRejectABeerIfTooYoung.json

    Under META-INF folder:

    • we group applications via groupId (e.g. com.example)
    • then each application is represented via the artifactId (e.g. beer-api-producer-git)
    • next, the version of the application. The version is mandatory! (e.g. 0.0.1-SNAPSHOT)
    • finally, there are two folders:

      • contracts - the good practice is to store the contracts required by each +consumer in the folder with the consumer name (e.g. beer-api-consumer). That way you +can use the stubs-per-consumer feature. Further directory structure is arbitrary.
      • mappings - in this folder the Maven / Gradle Spring Cloud Contract plugins will push +the stub server mappings. On the consumer side, Stub Runner will scan this folder +to start stub servers with stub definitions. The folder structure will be a copy +of the one created in the contracts subfolder.

    3.6.1 Protocol convention

    In order to control the type and location of the source of contracts (whether it’s +a binary storage or an SCM repository), you can use the protocol in the URL of +the repository. Spring Cloud Contract iterates over registered protocol resolvers +and tries to fetch the contracts (via a plugin) or stubs (via Stub Runner).

    For the SCM functionality, currently, we support the Git repository. To use it, +in the property, where the repository URL needs to be placed you just have to prefix +the connection URL with git://. Here you can find a couple of examples:

    git://file:///foo/bar
    +git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
    +git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git

    3.6.2 Producer

    For the producer, to use the SCM approach, we can reuse the +same mechanism we use for external contracts. We route Spring Cloud Contract +to use the SCM implementation via the URL that contains +the git:// protocol.

    [Important]Important

    You have to manually add the pushStubsToScm +goal in Maven or execute (bind) the pushStubsToScm task in +Gradle. We don’t push stubs to origin of your git +repository out of the box.

    Maven.  +

    <plugin>
    +    <groupId>org.springframework.cloud</groupId>
    +    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +    <version>${spring-cloud-contract.version}</version>
    +    <extensions>true</extensions>
    +    <configuration>
    +        <!-- Base class mappings etc. -->
    +
    +        <!-- We want to pick contracts from a Git repository -->
    +        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>
    +
    +        <!-- We reuse the contract dependency section to set up the path
    +        to the folder that contains the contract definitions. In our case the
    +        path will be /groupId/artifactId/version/contracts -->
    +        <contractDependency>
    +            <groupId>${project.groupId}</groupId>
    +            <artifactId>${project.artifactId}</artifactId>
    +            <version>${project.version}</version>
    +        </contractDependency>
    +
    +        <!-- The contracts mode can't be classpath -->
    +        <contractsMode>REMOTE</contractsMode>
    +    </configuration>
    +    <executions>
    +        <execution>
    +            <phase>package</phase>
    +            <goals>
    +                <!-- By default we will not push the stubs back to SCM,
    +                you have to explicitly add it as a goal -->
    +                <goal>pushStubsToScm</goal>
    +            </goals>
    +        </execution>
    +    </executions>
    +</plugin>

    +

    Gradle.  +

    contracts {
    +	// We want to pick contracts from a Git repository
    +	contractDependency {
    +		stringNotation = "${project.group}:${project.name}:${project.version}"
    +	}
    +	/*
    +	We reuse the contract dependency section to set up the path
    +	to the folder that contains the contract definitions. In our case the
    +	path will be /groupId/artifactId/version/contracts
    +	 */
    +	contractRepository {
    +		repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
    +	}
    +	// The mode can't be classpath
    +	contractsMode = "REMOTE"
    +	// Base class mappings etc.
    +}
    +
    +/*
    +In this scenario we want to publish stubs to SCM whenever
    +the `publish` task is executed
    +*/
    +publish.dependsOn("publishStubsToScm")

    +

    With such a setup:

    • Git project will be cloned to a temporary directory
    • The SCM stub downloader will go to META-INF/groupId/artifactId/version/contracts folder +to find contracts. E.g. for com.example:foo:1.0.0 the path would be +META-INF/com.example/foo/1.0.0/contracts
    • Tests will be generated from the contracts
    • Stubs will be created from the contracts
    • Once the tests pass, the stubs will be committed in the cloned repository
    • Finally, a push will be done to that repo’s origin

    3.6.3 Consumer

    On the consumer side when passing the repositoryRoot parameter, +either from the @AutoConfigureStubRunner annotation, the +JUnit rule or properties, it’s enough to pass the URL of the +SCM repository, prefixed with the protocol. For example

    @AutoConfigureStubRunner(
    +    stubsMode="REMOTE",
    +    repositoryRoot="git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
    +    ids="com.example:bookstore:0.0.1.RELEASE"
    +)

    With such a setup:

    • Git project will be cloned to a temporary directory
    • The SCM stub downloader will go to META-INF/groupId/artifactId/version/ folder +to find stub definitions and contracts. E.g. for com.example:foo:1.0.0 the path would be +META-INF/com.example/foo/1.0.0/
    • Stub servers will be started and fed with mappings
    • Messaging definitions will be read and used in the messaging tests

    3.7 Can I use the Pact Broker?

    When using Pact you can use the Pact Broker +to store and share Pact definitions. Starting from Spring Cloud Contract +2.0.0 one can fetch Pact files from the Pact Broker to generate +tests and stubs.

    As a prerequisite the Pact Converter and Pact Stub Downloader +are required. You have to add it via the spring-cloud-contract-pact dependency. +You can read more about it in the Section 10.1.1, “Pact Converter” section.

    [Important]Important

    Pact follows the Consumer Contract convention. That means +that the Consumer creates the Pact definitions first, then +shares the files with the Producer. Those expectations are generated +from the Consumer’s code and can break the Producer if the expectation +is not met.

    3.7.1 Pact Consumer

    The consumer uses Pact framework to generate Pact files. The +Pact files are sent to the Pact Broker. An example of such +setup can be found here.

    3.7.2 Producer

    For the producer, to use the Pact files from the Pact Broker, we can reuse the +same mechanism we use for external contracts. We route Spring Cloud Contract +to use the Pact implementation via the URL that contains +the pact:// protocol. It’s enough to pass the URL to the +Pact Broker. An example of such setup can be found here.

    Maven.  +

    <plugin>
    +    <groupId>org.springframework.cloud</groupId>
    +    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +    <version>${spring-cloud-contract.version}</version>
    +    <extensions>true</extensions>
    +    <configuration>
    +        <!-- Base class mappings etc. -->
    +
    +        <!-- We want to pick contracts from a Git repository -->
    +        <contractsRepositoryUrl>pact://http://localhost:8085</contractsRepositoryUrl>
    +
    +        <!-- We reuse the contract dependency section to set up the path
    +        to the folder that contains the contract definitions. In our case the
    +        path will be /groupId/artifactId/version/contracts -->
    +        <contractDependency>
    +            <groupId>${project.groupId}</groupId>
    +            <artifactId>${project.artifactId}</artifactId>
    +            <!-- When + is passed, a latest tag will be applied when fetching pacts -->
    +            <version>+</version>
    +        </contractDependency>
    +
    +        <!-- The contracts mode can't be classpath -->
    +        <contractsMode>REMOTE</contractsMode>
    +    </configuration>
    +    <!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.springframework.cloud</groupId>
    +            <artifactId>spring-cloud-contract-pact</artifactId>
    +            <version>${spring-cloud-contract.version}</version>
    +        </dependency>
    +    </dependencies>
    +</plugin>

    +

    Gradle.  +

    buildscript {
    +	repositories {
    +		//...
    +	}
    +
    +	dependencies {
    +		// ...
    +		// Don't forget to add spring-cloud-contract-pact to the classpath!
    +		classpath "org.springframework.cloud:spring-cloud-contract-pact:${contractVersion}"
    +	}
    +}
    +
    +contracts {
    +	// When + is passed, a latest tag will be applied when fetching pacts
    +	contractDependency {
    +		stringNotation = "${project.group}:${project.name}:+"
    +	}
    +	contractRepository {
    +		repositoryUrl = "pact://http://localhost:8085"
    +	}
    +	// The mode can't be classpath
    +	contractsMode = "REMOTE"
    +	// Base class mappings etc.
    +}

    +

    With such a setup:

    • Pact files will be downloaded from the Pact Broker
    • Spring Cloud Contract will convert the Pact files into tests and stubs
    • The JAR with the stubs gets automatically created as usual

    3.7.3 Pact Consumer (Producer Contract approach)

    In the scenario where you don’t want to do Consumer Contract approach +(for every single consumer define the expectations) but you’d prefer +to do Producer Contracts (the producer provides the contracts and +publishes stubs), it’s enough to use Spring Cloud Contract with +Stub Runner option. An example of such setup can be found here.

    First, remember to add Stub Runner and Spring Cloud Contract Pact module +as test dependencies.

    Maven.  +

    <dependencyManagement>
    +    <dependencies>
    +        <dependency>
    +            <groupId>org.springframework.cloud</groupId>
    +            <artifactId>spring-cloud-dependencies</artifactId>
    +            <version>${spring-cloud.version}</version>
    +            <type>pom</type>
    +            <scope>import</scope>
    +        </dependency>
    +    </dependencies>
    +</dependencyManagement>
    +
    +<!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
    +<dependencies>
    +    <!-- ... -->
    +    <dependency>
    +        <groupId>org.springframework.cloud</groupId>
    +        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    +        <scope>test</scope>
    +    </dependency>
    +    <dependency>
    +        <groupId>org.springframework.cloud</groupId>
    +        <artifactId>spring-cloud-contract-pact</artifactId>
    +        <scope>test</scope>
    +    </dependency>
    +</dependencies>

    +

    Gradle.  +

    dependencyManagement {
    +    imports {
    +        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    +    }
    +}
    +
    +dependencies {
    +    //...
    +    testCompile("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
    +    // Don't forget to add spring-cloud-contract-pact to the classpath!
    +    testCompile("org.springframework.cloud:spring-cloud-contract-pact")
    +}

    +

    Next, just pass the URL of the Pact Broker to repositoryRoot, prefixed +with pact:// protocol. E.g. pact://http://localhost:8085

    @RunWith(SpringRunner.class)
    +@SpringBootTest
    +@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE,
    +		ids = "com.example:beer-api-producer-pact",
    +		repositoryRoot = "pact://http://localhost:8085")
    +public class BeerControllerTest {
    +    //Inject the port of the running stub
    +    @StubRunnerPort("beer-api-producer-pact") int producerPort;
    +    //...
    +}

    With such a setup:

    • Pact files will be downloaded from the Pact Broker
    • Spring Cloud Contract will convert the Pact files into stub definitions
    • The stub servers will be started and fed with stubs

    For more information about Pact support you can go to +the Section 10.7, “Using the Pact Stub Downloader” section.

    3.8 How can I debug the request/response being sent by the generated tests client?

    The generated tests all boil down to RestAssured in some form or fashion which relies on Apache HttpClient. HttpClient has a facility called wire logging which logs the entire request and response to HttpClient. Spring Boot has a logging common application property for doing this sort of thing, just add this to your application properties

    logging.level.org.apache.http.wire=DEBUG

    3.8.1 How can I debug the mapping/request/response being sent by WireMock?

    Starting from version 1.2.0 we turn on WireMock logging to info and the WireMock notifier to being verbose. Now you will exactly know what request was received by WireMock server and which -matching response definition was picked.

    To turn off this feature just bump WireMock logging to ERROR

    logging.level.com.github.tomakehurst.wiremock=ERROR

    3.6.2 How can I see what got registered in the HTTP server stub?

    You can use the mappingsOutputFolder property on @AutoConfigureStubRunner or StubRunnerRule +matching response definition was picked.

    To turn off this feature just bump WireMock logging to ERROR

    logging.level.com.github.tomakehurst.wiremock=ERROR

    3.8.2 How can I see what got registered in the HTTP server stub?

    You can use the mappingsOutputFolder property on @AutoConfigureStubRunner or StubRunnerRule to dump all mappings per artifact id. Also the port at which the given stub server was -started will be attached.

    3.6.3 Can I reference the request from the response?

    Yes! With version 1.1.0 we’ve added such a possibility. On the HTTP stub server side we’re providing support -for this for WireMock. In case of other HTTP server stubs you’ll have to implement the approach yourself.

    3.6.4 Can I reference text from file?

    Yes! With version 1.2.0 we’ve added such a possibility. It’s enough to call file(…​) method in the -DSL and provide a path relative to where the contract lays.

    4. Spring Cloud Contract Verifier Setup

    You can set up Spring Cloud Contract Verifier in either of two ways

    4.1 Gradle Project

    To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the -following sections:

    4.1.1 Prerequisites

    In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a +started will be attached.

    3.8.3 Can I reference text from file?

    Yes! With version 1.2.0 we’ve added such a possibility. It’s enough to call file(…​) method in the +DSL and provide a path relative to where the contract lays. +If you’re using YAML just use the bodyFromFile property.

    4. Spring Cloud Contract Verifier Setup

    You can set up Spring Cloud Contract Verifier in the following ways:

    4.1 Gradle Project

    To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the +following sections:

    4.1.1 Prerequisites

    In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a Gradle or a Maven plugin.

    [Warning]Warning

    If you want to use Spock in your projects, you must add separately the spock-core and spock-spring modules. Check Spock docs for more information

    4.1.2 Add Gradle Plugin with Dependencies

    To add a Gradle plugin with dependencies, use code similar to this:

    buildscript {
    @@ -882,7 +1554,8 @@ GroovyDSL. By default, its value is $rootDir/src/test/reso
     from the Groovy DSL should be placed. By default its value is
     $buildDir/generated-test-sources/contractVerifier.
  • stubsOutputDir: Specifies the directory where the generated WireMock stubs from the Groovy DSL should be placed.
  • targetFramework: Specifies the target test framework to be used. Currently, Spock and -JUnit are supported with JUnit being the default framework.
  • The following properties are used when you want to specify the location of the JAR +JUnit are supported with JUnit being the default framework.

  • contractsProperties: a map containing properties to be passed to Spring Cloud Contract +components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.
  • The following properties are used when you want to specify the location of the JAR containing the contracts: * contractDependency: Specifies the Dependency that provides groupid:artifactid:version:classifier coordinates. You can use the contractDependency @@ -890,9 +1563,12 @@ closure to set it up. * contractsPath: Specifies the path to the jar. If contract dependencies are downloaded, the path defaults to groupid/artifactid where groupid is slash separated. Otherwise, it scans contracts under the provided directory. -* contractsWorkOffline: Specifies whether to download the dependencies each time, so -that you can work online. In other words, it specifies whether to reuses the local Maven -repo.

    4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base +* contractsMode: Specifies the mode of downloading contracts (whether the +JAR is available offline, remotely etc.) +* contractsSnapshotCheckSkip: If set to true will not assert whether the +downloaded stubs / contract JAR was downloaded from a remote location or a local one(only applicable to Maven repos, not Git or Pact). +* deleteStubsAfterTest: If set to false will not remove any downloaded +contracts from temporary directories

    4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an endpoint, which should be verified.

    abstract class BaseMockMvcSpec extends Specification {
     
    @@ -930,7 +1606,14 @@ baseClassMappings {
      - src/test/resources/contract/foo/

    By providing the baseClassForTests, we have a fallback in case mapping did not succeed. (You could also provide the packageWithBaseClasses as a fallback.) That way, the tests generated from src/test/resources/contract/com/ contracts extend the -com.example.ComBase, whereas the rest of the tests extend com.example.FooBase.

    4.1.12 Invoking Generated Tests

    To ensure that the provider side is compliant with defined contracts, you need to invoke:

    ./gradlew generateContractTests test

    4.1.13 Spring Cloud Contract Verifier on the Consumer Side

    In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin +com.example.ComBase, whereas the rest of the tests extend com.example.FooBase.

    4.1.12 Invoking Generated Tests

    To ensure that the provider side is compliant with defined contracts, you need to invoke:

    ./gradlew generateContractTests test

    4.1.13 Pushing stubs to SCM

    If you’re using the SCM repository to keep the contracts and +stubs, you might want to automate the step of pushing stubs to +the repository. To do that, it’s enough to call the pushStubsToScm +task. Example:

    $ ./gradlew pushStubsToScm

    Under Section 10.6, “Using the SCM Stub Downloader” you can find all possible +configuration options that you can pass either via +the contractsProperties field e.g. contracts { contractsProperties = [foo:"bar"] }, +via contractsProperties method e.g. contracts { contractsProperties([foo:"bar"]) }, +a system property or an environment variable.

    4.1.14 Spring Cloud Contract Verifier on the Consumer Side

    In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin in exactly the same way as in case of provider. If you do not want to use Stub Runner then you need to copy contracts stored in src/test/resources/contracts and generate WireMock JSON stubs using:

    ./gradlew generateClientStubs
    [Note]Note

    The stubsOutputDir option has to be set for stub generation to work.

    When present, JSON stubs can be used in automated tests of consuming a service.

    @ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)
    @@ -955,7 +1638,7 @@ WireMock JSON stubs using:

    ./gradlew generateClie
      }
     }

    LoanApplication makes a call to FraudDetection service. This request is handled by a WireMock server configured with stubs generated by Spring Cloud Contract Verifier.

    4.2 Maven Project

    To learn how to set up the Maven project for Spring Cloud Contract Verifier, read the -following sections:

    4.2.1 Add maven plugin

    Add the Spring Cloud Contract BOM in a fashion similar to this:

    <dependencyManagement>
    +following sections:

    4.2.1 Add maven plugin

    Add the Spring Cloud Contract BOM in a fashion similar to this:

    <dependencyManagement>
     	<dependencies>
     		<dependency>
     			<groupId>org.springframework.cloud</groupId>
    @@ -974,8 +1657,8 @@ following sections:

      <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> </configuration> </plugin>

    You can read more in the -Spring -Cloud Contract Maven Plugin Documentation.

    4.2.2 Maven and Rest Assured 2.0

    By default, Rest Assured 3.x is added to the classpath. However, you can use Rest +Spring +Cloud Contract Maven Plugin Documentation (example for 2.0.0.RELEASE version).

    4.2.2 Maven and Rest Assured 2.0

    By default, Rest Assured 3.x is added to the classpath. However, you can use Rest Assured 2.x by adding it to the plugins classpath, as shown here:

    <plugin>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    @@ -1121,10 +1804,12 @@ the matched contract. For example, if you have a contract under
     src/test/resources/contract/foo/bar/baz/ and map the property
     .* → com.example.base.BaseClass, then the test class generated from these contracts
     extends com.example.base.BaseClass. This setting takes precedence over
    -packageWithBaseClasses and baseClassForTests.

    If you want to download your contract definitions from a Maven repository, you can use +packageWithBaseClasses and baseClassForTests.

  • contractsProperties: a map containing properties to be passed to Spring Cloud Contract +components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.
  • If you want to download your contract definitions from a Maven repository, you can use the following options:

    • contractDependency: The contract dependency that contains all the packaged contracts.
    • contractsPath: The path to the concrete contracts in the JAR with packaged contracts. -Defaults to groupid/artifactid where gropuid is slash separated.
    • contractsWorkOffline: Dictates whether the dependencies should be downloaded or the -local Maven artifacts should be reused.
    • contractsRepositoryUrl: URL to a repo with the artifacts that have contracts. If it is not provided, +Defaults to groupid/artifactid where gropuid is slash separated.
    • contractsMode: Picks the mode in which stubs will be found and registered
    • contractsSnapshotCheckSkip: If true then will not assert whether a stub / contract +JAR was downloaded from local or remote location
    • deleteStubsAfterTest: If set to false will not remove any downloaded +contracts from temporary directories
    • contractsRepositoryUrl: URL to a repo with the artifacts that have contracts. If it is not provided, use the current Maven ones.
    • contractsRepositoryUsername: The user name to be used to connect to the repo with contracts.
    • contractsRepositoryPassword: The password to be used to connect to the repo with contracts.
    • contractsRepositoryProxyHost: The proxy host to be used to connect to the repo with contracts.
    • contractsRepositoryProxyPort: The proxy port to be used to connect to the repo with contracts.

    We cache only non-snapshot, explicitly provided versions (for example + or 1.0.0.BUILD-SNAPSHOT won’t get cached). By default, this feature is turned on.

    4.2.8 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an @@ -1134,13 +1819,51 @@ endpoint, which should be verified.

    import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc
     import spock.lang.Specification
     
    -class  MvcSpec extends Specification {
    +class MvcSpec extends Specification {
       def setup() {
        RestAssuredMockMvc.standaloneSetup(new ExampleSpringController())
       }
    -}

    If you use Explicit mode, you can use a base class to initialize the whole tested app -similarly, as you might find in regular integration tests. If you use the JAXRSCLIENT -mode, this base class should also contain a protected WebTarget webTarget field. Right +}

    You can also setup the whole context if necessary.

    import io.restassured.module.mockmvc.RestAssuredMockMvc;
    +import org.junit.Before;
    +import org.junit.runner.RunWith;
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.boot.test.context.SpringBootTest;
    +import org.springframework.test.context.junit4.SpringRunner;
    +import org.springframework.web.context.WebApplicationContext;
    +
    +@RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
    +public abstract class BaseTestClass {
    +
    +	@Autowired
    +	WebApplicationContext context;
    +
    +	@Before
    +	public void setup() {
    +		RestAssuredMockMvc.webAppContextSetup(this.context);
    +	}
    +}

    If you use EXPLICIT mode, you can use a base class to initialize the whole tested app +similarly, as you might find in regular integration tests.

    import io.restassured.RestAssured;
    +import org.junit.Before;
    +import org.junit.runner.RunWith;
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.boot.test.context.SpringBootTest;
    +import org.springframework.boot.web.server.LocalServerPort
    +import org.springframework.test.context.junit4.SpringRunner;
    +import org.springframework.web.context.WebApplicationContext;
    +
    +@RunWith(SpringRunner.class)
    +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")
    +public abstract class BaseTestClass {
    +
    +	@LocalServerPort
    +	int port;
    +
    +	@Before
    +	public void setup() {
    +		RestAssured.baseURI = "http://localhost:" + this.port;
    +	}
    +}

    If you use the JAXRSCLIENT mode, this base class should also contain a protected WebTarget webTarget field. Right now, the only option to test the JAX-RS API is to start a web server.

    4.2.9 Different base classes for contracts

    If your base classes differ between contracts, you can tell the Spring Cloud Contract plugin which class should get extended by the autogenerated tests. You have two options:

    • Follow a convention by providing the packageWithBaseClasses
    • provide explicit mapping via baseClassMappings

    By Convention

    The convention is such that if you have a contract under (for example) src/test/resources/contract/foo/bar/baz/ and set the value of the @@ -1204,7 +1927,46 @@ goal.

    For Groovy Spock code, use the following:

    </testSources>
     	</configuration>
     </plugin>

    To ensure that provider side is compliant with defined contracts, you need to invoke -mvn generateTest test.

    4.2.11 Maven Plugin and STS

    If you see the following exception while using STS:

    STS Exception

    When you click on the error marker you should see something like this:

     plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
    +mvn generateTest test.

    4.2.11 Pushing stubs to SCM

    If you’re using the SCM repository to keep the contracts and +stubs, you might want to automate the step of pushing stubs to +the repository. To do that, it’s enough to add the pushStubsToScm +goal. Example:

    <plugin>
    +    <groupId>org.springframework.cloud</groupId>
    +    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +    <version>${spring-cloud-contract.version}</version>
    +    <extensions>true</extensions>
    +    <configuration>
    +        <!-- Base class mappings etc. -->
    +
    +        <!-- We want to pick contracts from a Git repository -->
    +        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>
    +
    +        <!-- We reuse the contract dependency section to set up the path
    +        to the folder that contains the contract definitions. In our case the
    +        path will be /groupId/artifactId/version/contracts -->
    +        <contractDependency>
    +            <groupId>${project.groupId}</groupId>
    +            <artifactId>${project.artifactId}</artifactId>
    +            <version>${project.version}</version>
    +        </contractDependency>
    +
    +        <!-- The contracts mode can't be classpath -->
    +        <contractsMode>REMOTE</contractsMode>
    +    </configuration>
    +    <executions>
    +        <execution>
    +            <phase>package</phase>
    +            <goals>
    +                <!-- By default we will not push the stubs back to SCM,
    +                you have to explicitly add it as a goal -->
    +                <goal>pushStubsToScm</goal>
    +            </goals>
    +        </execution>
    +    </executions>
    +</plugin>

    Under Section 10.6, “Using the SCM Stub Downloader” you can find all possible +configuration options that you can pass either via +the <configuration><contractProperties> map, a system property +or an environment variable.

    4.2.12 Maven Plugin and STS

    If you see the following exception while using STS:

    STS Exception

    When you click on the error marker you should see something like this:

     plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
      cloud-contract-maven-plugin:1.1.0.M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145) at
      org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:331) at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1362) at
     ...
    @@ -1241,48 +2003,7 @@ goal.

    For Groovy Spock code, use the following:

    </plugin>
             </plugins>
         </pluginManagement>
    -</build>

    4.2.12 Spring Cloud Contract Verifier on the Consumer Side

    You can also use the Spring Cloud Contract Verifier for the consumer side. To do so, use -the plugin so that it only converts the contracts and generates the stubs. To achieve -that, you need to configure Spring Cloud Contract Verifier plugin in exactly the same way -as you would for a provider. You need to copy contracts stored in -src/test/resources/contracts and generate WireMock JSON stubs using the -mvn generateStubs command. By default, the generated WireMock mapping is stored in a -directory named target/mappings. From these generated mappings, your project should -create additional artifacts with a classifier of stubs for easy deployment to the maven -repository.

    Here is a sample configuration:

    <plugin>
    -    <groupId>org.springframework.cloud</groupId>
    -    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    -    <version>${verifier-plugin.version}</version>
    -    <executions>
    -        <execution>
    -            <goals>
    -                <goal>convert</goal>
    -                <goal>generateStubs</goal>
    -            </goals>
    -        </execution>
    -    </executions>
    -</plugin>

    When present, JSON stubs can be used in consumer automated tests, as shown here:

    @RunWith(SpringTestRunner.class)
    -@SpringBootTest
    -@AutoConfigureStubRunner
    -public class LoanApplicationServiceTests {
    -
    -  @Autowired
    -  LoanApplicationService service;
    -
    -  @Test
    -  public void shouldSuccessfullyApplyForLoan() {
    -    //given:
    - 	LoanApplication application =
    -			new LoanApplication(new Client("12345678901"), 123.123);
    -    //when:
    -	LoanApplicationResult loanApplication = service.loanApplication(application);
    -    // then:
    -	assertThat(loanApplication.loanApplicationStatus).isEqualTo(LoanApplicationStatus.LOAN_APPLIED);
    -	assertThat(loanApplication.rejectionReason).isNull();
    -  }
    -}

    LoanApplication makes a call to the FraudDetection service. This request is handled -by a WireMock server configured with stubs generated by the Spring Cloud Contract -Verifier.

    4.3 Stubs and Transitive Dependencies

    The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One +</build>

    4.3 Stubs and Transitive Dependencies

    The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One problem that arises is that, when reusing the stubs, you can mistakenly import all of that stub’s dependencies. When building a Maven artifact, even though you have a couple of different jars, all of them share one pom:

    ├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar
    @@ -1299,15 +2020,102 @@ when you include the github-webhook stubs in anothe
     dependency gets downloaded by Stub Runner) then, since all of the dependencies are
     optional, they will not get downloaded.

    Create a separate artifactid for the stubs

    If you create a separate artifactid, then you can set it up in whatever way you wish. For example, you might decide to have no dependencies at all.

    Exclude dependencies on the consumer side

    As a consumer, if you add the stub dependency to your classpath, you can explicitly -exclude the unwanted dependencies.

    4.4 Scenarios

    You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to +exclude the unwanted dependencies.

    4.4 CI Server setup

    When fetching stubs / contracts in a CI, shared environment, what might happen is that +both the producer and the consumer reuse the same local Maven repository. Due to this, +the framework, responsible for downloading a stub JAR from remote location, +can’t decide which JAR should be picked, local or remote one. That caused +the "The artifact was found in the local repository but you have explicitly +stated that it should be downloaded from a remote one" exception +and failed the build.

    For such cases we’re introducing the property and plugin setup mechanism:

    • via stubrunner.snapshot-check-skip system property
    • via STUBRUNNER_SNAPSHOT_CHECK_SKIP environment variable

    if either of these values is set to true, then the stub downloader will not +verify the origin of the downloaded JAR.

    For the plugins you need to set the contractsSnapshotCheckSkip property +to true.

    4.5 Scenarios

    You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to stick to the proper naming convention while creating your contracts. The convention -requires including an order number followed by an underscore, as shown in this example:

    my_contracts_dir\
    +requires including an order number followed by an underscore. This will work regardles
    + of whether you’re working with YAML or Groovy. Example:

    my_contracts_dir\
       scenario1\
         1_login.groovy
         2_showCart.groovy
         3_logout.groovy

    Such a tree causes Spring Cloud Contract Verifier to generate WireMock’s scenario with a name of scenario1 and the three following steps:

    1. login marked as Started pointing to…​
    2. showCart marked as Step1 pointing to…​
    3. logout marked as Step2 which will close the scenario.

    More details about WireMock scenarios can be found at -http://wiremock.org/stateful-behaviour.html

    Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.

    5. Spring Cloud Contract Verifier Messaging

    Spring Cloud Contract Verifier lets you verify applications that uses messaging as a +http://wiremock.org/stateful-behaviour.html

    Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.

    4.6 Docker Project

    We’re publishing a springcloud/spring-cloud-contract Docker image +that contains a project that will generate tests and execute them in EXPLICIT mode +against a running application.

    [Tip]Tip

    The EXPLICIT mode means that the tests generated from contracts will send +real requests and not the mocked ones.

    4.6.1 Short intro to Maven, JARs and Binary storage

    Since the Docker image can be used by non JVM projects, it’s good to +explain the basic terms behind Spring Cloud Contract packaging defaults.

    Part of the following definitions were taken from the Maven Glossary

    • Project: Maven thinks in terms of projects. Everything that you +will build are projects. Those projects follow a well defined +“Project Object Model”. Projects can depend on other projects, +in which case the latter are called “dependencies”. A project may +consistent of several subprojects, however these subprojects are still +treated equally as projects.
    • Artifact: An artifact is something that is either produced or used +by a project. Examples of artifacts produced by Maven for a project +include: JARs, source and binary distributions. Each artifact +is uniquely identified by a group id and an artifact ID which is +unique within a group.
    • JAR: JAR stands for Java ARchive. It’s a format based on +the ZIP file format. Spring Cloud Contract packages the contracts and generated +stubs in a JAR file.
    • GroupId: A group ID is a universally unique identifier for a project. +While this is often just the project name (eg. commons-collections), +it is helpful to use a fully-qualified package name to distinguish it +from other projects with a similar name (eg. org.apache.maven). +Typically, when published to the Artifact Manager, the GroupId will get +slash separated and form part of the URL. E.g. for group id com.example +and artifact id application would be /com/example/application/.
    • Classifier: The Maven dependency notation looks as follows: +groupId:artifactId:version:classifier. The classifier is additional suffix +passed to the dependency. E.g. stubs, sources. The same dependency +e.g. com.example:application can produce multiple artifacts that +differ from each other with the classifier.
    • Artifact manager: When you generate binaries / sources / packages, you would +like them to be available for others to download / reference or reuse. In case +of the JVM world those artifacts would be JARs, for Ruby these are gems +and for Docker those would be Docker images. You can store those artifacts +in a manager. Examples of such managers can be Artifactory +or Nexus.

    4.6.2 How it works

    The image searches for contracts under the /contracts folder. +The output from running the tests will be available under +/spring-cloud-contract/build folder (it’s useful for debugging +purposes).

    It’s enough for you to mount your contracts, pass the environment variables + and the image will:

    • generate the contract tests
    • execute the tests against the provided URL
    • generate the WireMock stubs
    • (optional - turned on by default) publish the stubs to a Artifact Manager

    Environment Variables

    The Docker image requires some environment variables to point to +your running application, to the Artifact manager instance etc.

    • PROJECT_GROUP - your project’s group id. Defaults to com.example
    • PROJECT_VERSION - your project’s version. Defaults to 0.0.1-SNAPSHOT
    • PROJECT_NAME - artifact id. Defaults to example
    • REPO_WITH_BINARIES_URL - URL of your Artifact Manager. Defaults to http://localhost:8081/artifactory/libs-release-local +which is the default URL of Artifactory running locally
    • REPO_WITH_BINARIES_USERNAME - (optional) username when the Artifact Manager is secured
    • REPO_WITH_BINARIES_PASSWORD - (optional) password when the Artifact Manager is secured
    • PUBLISH_ARTIFACTS - if set to true then will publish artifact to binary storage. Defaults to true.

    These environment variables are used when contracts lay in an external repository. To enable +this feature you must set the EXTERNAL_CONTRACTS_ARTIFACT_ID environment variable.

    • EXTERNAL_CONTRACTS_GROUP_ID - group id of the project with contracts. Defaults to com.example
    • EXTERNAL_CONTRACTS_ARTIFACT_ID- artifact id of the project with contracts.
    • EXTERNAL_CONTRACTS_CLASSIFIER- classifier of the project with contracts. Empty by default
    • EXTERNAL_CONTRACTS_VERSION - version of the project with contracts. Defaults to +, equivalent to picking the latest
    • EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL - URL of your Artifact Manager. Defaults to value of REPO_WITH_BINARIES_URL env var. +If that’s not set, defaults to http://localhost:8081/artifactory/libs-release-local +which is the default URL of Artifactory running locally
    • EXTERNAL_CONTRACTS_PATH - path to contracts for the given project, inside the project with contracts. +Defaults to slash separated EXTERNAL_CONTRACTS_GROUP_ID concatenated with / and EXTERNAL_CONTRACTS_ARTIFACT_ID. E.g. +for group id foo.bar and artifact id baz, would result in foo/bar/baz contracts path.
    • EXTERNAL_CONTRACTS_WORK_OFFLINE - if set to true then will retrieve artifact with contracts +from the container’s .m2. Mount your local .m2 as a volume available at the container’s /root/.m2 path. +You must not set both EXTERNAL_CONTRACTS_WORK_OFFLINE and EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL.

    These environment variables are used when tests are executed:

    • APPLICATION_BASE_URL - url against which tests should be executed. +Remember that it has to be accessible from the Docker container (e.g. localhost +will not work)
    • APPLICATION_USERNAME - (optional) username for basic authentication to your application
    • APPLICATION_PASSWORD - (optional) password for basic authentication to your application

    4.6.3 Example of usage

    Let’s take a look at a simple MVC application

    $ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
    +$ cd bookstore

    The contracts are available under /contracts folder.

    4.6.4 Server side (nodejs)

    Since we want to run tests, we could just execute:

    $ npm test

    however, for learning purposes, let’s split it into pieces:

    # Stop docker infra (nodejs, artifactory)
    +$ ./stop_infra.sh
    +# Start docker infra (nodejs, artifactory)
    +$ ./setup_infra.sh
    +
    +# Kill & Run app
    +$ pkill -f "node app"
    +$ nohup node app &
    +
    +# Prepare environment variables
    +$ SC_CONTRACT_DOCKER_VERSION="..."
    +$ APP_IP="192.168.0.100"
    +$ APP_PORT="3000"
    +$ ARTIFACTORY_PORT="8081"
    +$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
    +$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
    +$ CURRENT_DIR="$( pwd )"
    +$ CURRENT_FOLDER_NAME=${PWD##*/}
    +$ PROJECT_VERSION="0.0.1.RELEASE"
    +
    +# Execute contract tests
    +$ docker run  --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"
    +
    +# Kill app
    +$ pkill -f "node app"

    What will happen is that via bash scripts:

    • infrastructure will be set up (MongoDb, Artifactory). +In real life scenario you would just run the NodeJS application +with mocked database. In this example we want to show how we can +benefit from Spring Cloud Contract in no time.
    • due to those constraints the contracts also represent the +stateful situation

      • first request is a POST that causes data to get inserted to the database
      • second request is a GET that returns a list of data with 1 previously inserted element
    • the NodeJS application will be started (on port 3000)
    • contract tests will be generated via Docker and tests +will be executed against the running application

      • the contracts will be taken from /contracts folder.
      • the output of the test execution is available under +node_modules/spring-cloud-contract/output.
    • the stubs will be uploaded to Artifactory. You can check them out +under http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ . +The stubs will be here http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar.

    To see how the client side looks like check out the Section 6.9, “Stub Runner Docker” section.

    5. Spring Cloud Contract Verifier Messaging

    Spring Cloud Contract Verifier lets you verify applications that use messaging as a means of communication. All of the integrations shown in this document work with Spring, but you can also create one of your own and use that.

    5.1 Integrations

    You can use one of the following four integration configurations:

    • Apache Camel
    • Spring Integration
    • Spring Cloud Stream
    • Spring AMQP

    Since we use Spring Boot, if you have added one of these libraries to the classpath, all the messaging configuration is automatically set up.

    [Important]Important

    Remember to put @AutoConfigureMessageVerifier on the base class of your @@ -1342,7 +2150,8 @@ message is triggered by a component inside the application (for example, schedu meanings for different messaging implementations. For Stream and Integration it is first resolved as a destination of a channel. Then, if there is no such destination it is resolved as a channel name. For Camel, that’s a certain component (for example, -jms).

    5.3.1 Scenario 1: No Input Message

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +jms).

    5.3.1 Scenario 1: No Input Message

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		triggeredBy('bookReturnedTriggered()')
    @@ -1355,7 +2164,19 @@ it is resolved as a channel name. For Camel

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  triggeredBy: bookReturnedTriggered
    +outputMessage:
    +  sentTo: activemq:output
    +  body:
    +    bookName: foo
    +  headers:
    +    BOOK-NAME: foo
    +    contentType: application/json

    +

    The following JUnit test is created:

    '''
      // when:
       bookReturnedTriggered();
     
    @@ -1382,7 +2203,8 @@ it is resolved as a channel name. For Camel"bookName").isEqualTo("foo")
     
    -'''

    5.3.2 Scenario 2: Output Triggered by Input

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +'''

    5.3.2 Scenario 2: Output Triggered by Input

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		messageFrom('jms:input')
    @@ -1402,7 +2224,22 @@ it is resolved as a channel name. For Camel'BOOK-NAME', 'foo')
     		}
     	}
    -}

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  messageFrom: jms:input
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +outputMessage:
    +  sentTo: jms:output
    +  body:
    +    bookName: foo
    +  headers:
    +    BOOK-NAME: foo

    +

    The following JUnit test is created:

    '''
     // given:
      ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
       "{\\"bookName\\":\\"foo\\"}"
    @@ -1437,7 +2274,8 @@ then:
     and:
        DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
        assertThatJson(parsedJson).field("bookName").isEqualTo("foo")
    -"""

    5.3.3 Scenario 3: No Output Message

    Here is an example for Camel. For the given contract:

    def contractDsl = Contract.make {
    +"""

    5.3.3 Scenario 3: No Output Message

    For the given contract:

    Groovy DSL.  +

    def contractDsl = Contract.make {
     	label 'some_label'
     	input {
     		messageFrom('jms:delete')
    @@ -1449,7 +2287,17 @@ and:
     		}
     		assertThat('bookWasDeleted()')
     	}
    -}

    The following JUnit test is created:

    '''
    +}

    +

    YAML.  +

    label: some_label
    +input:
    +  messageFrom: jms:delete
    +  messageBody:
    +    bookName: 'foo'
    +  messageHeaders:
    +    sample: header
    +  assertThat: bookWasDeleted()

    +

    The following JUnit test is created:

    '''
     // given:
      ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
     	"{\\"bookName\\":\\"foo\\"}"
    @@ -1475,9 +2323,7 @@ then:
     	 noExceptionThrown()
     	 bookWasDeleted()
     '''

    5.4 Consumer Stub Generation

    Unlike the HTTP part, in messaging, we need to publish the Groovy DSL inside the JAR with -a stub. Then it is parsed on the consumer side and proper stubbed routes are created.

    For more information, see -the -Stub Runner Messaging sections.

    Maven.  +a stub. Then it is parsed on the consumer side and proper stubbed routes are created.

    For more information, see ??? section.

    Maven. 

    <dependencies>
     	<dependency>
     		<groupId>org.springframework.cloud</groupId>
    @@ -1593,7 +2439,7 @@ versions, which are automatically uploaded after every successful build:

    "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/release" } }

    -

    6.2 Publishing Stubs as JARs

    The easiest approach would be to centralize the way stubs are kept. For example, you can +

    6.2 Publishing Stubs as JARs

    The easiest approach would be to centralize the way stubs are kept. For example, you can keep them as jars in a Maven repository.

    [Tip]Tip

    For both Maven and Gradle, the setup comes ready to work. However, you can customize it if you want to.

    Maven. 

    <!-- First disable the default jar setup in the properties section -->
    @@ -1615,7 +2461,9 @@ it if you want to.

    Maven.  <inherited>false</inherited> <configuration> <attach>true</attach> - <descriptor>${basedir}/src/assembly/stub.xml</descriptor> + <descriptors> + ${basedir}/src/assembly/stub.xml + </descriptors> </configuration> </execution> </executions> @@ -1683,12 +2531,10 @@ publishing { }

    6.3 Stub Runner Core

    Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of Consumer Driven Contracts.

    Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions. -For messaging, special stub routes are defined.

    6.3.1 Retrieving stubs

    You can pick the following options of acquiring stubs

    • Aether based solution that downloads JARs with stubs from Artifactory / Nexus
    • Classpath scanning solution that searches classpath via pattern to retrieve stubs
    • Write your own implementation of the org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder for full customization

    The latter example is described in the Custom Stub Runner section.

    Stub downloading

    If you provide the stubrunner.repositoryRoot or stubrunner.workOffline flag will be set -to true then Stub Runner will connect to the given server and download the required jars. -It will then unpack the JAR to a temporary folder and reference those files in further -contract processing.

    Example:

    @AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095")

    Classpath scanning

    If you DON’T provide the stubrunner.repositoryRoot and stubrunner.workOffline flag will -be set to false (that’s the default) then classpath will get scanned. Let’s look at the -following example:

    @AutoConfigureStubRunner(ids = {
    +For messaging, special stub routes are defined.

    6.3.1 Retrieving stubs

    You can pick the following options of acquiring stubs

    • Aether based solution that downloads JARs with stubs from Artifactory / Nexus
    • Classpath scanning solution that searches classpath via pattern to retrieve stubs
    • Write your own implementation of the org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder for full customization

    The latter example is described in the Custom Stub Runner section.

    Stub downloading

    You can control the stub downloading via the stubsMode switch. It picks value from the +StubRunnerProperties.StubsMode enum. You can use the following options

    • StubRunnerProperties.StubsMode.CLASSPATH (default value) - will pick stubs from the classpath
    • StubRunnerProperties.StubsMode.LOCAL - will pick stubs from a local storage (e.g. .m2)
    • StubRunnerProperties.StubsMode.REMOTE - will pick stubs from a remote location

    Example:

    @AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)

    Classpath scanning

    If you set the stubsMode property to StubRunnerProperties.StubsMode.CLASSPATH +(or set nothing since CLASSPATH is the default value) then classpath will get scanned. +Let’s look at the following example:

    @AutoConfigureStubRunner(ids = {
         "com.example:beer-api-producer:+:stubs:8095",
         "com.example.foo:bar:1.0.0:superstubs:8096"
     })

    If you’ve added the dependencies to your classpath

    Maven.  @@ -1733,8 +2579,8 @@ producer stubs.

    The producer would setup the contr    └── com.example       └── beer-api-producer-restdocs       └── nested -       └── contract3.groovy

    To achieve proper stub packaging.

    Or using the Maven assembly plugin or -Gradle Jar task you have to create the following +       └── contract3.groovy

    To achieve proper stub packaging.

    Or using the Maven assembly plugin or +Gradle Jar task you have to create the following structure in your stubs jar.

    └── META-INF
         └── com.example
             └── beer-api-producer-restdocs
    @@ -1773,10 +2619,10 @@ HTTP stubs without the need to download artifacts.

    'false'

    HTTP Stubs

    Stubs are defined in JSON documents, whose syntax is defined in WireMock documentation

    Example:

    {
    +                                  repository

    HTTP Stubs

    Stubs are defined in JSON documents, whose syntax is defined in WireMock documentation

    Example:

    {
         "request": {
             "method": "GET",
             "url": "/ping"
    @@ -1852,6 +2698,7 @@ Check their 
     	Map<StubConfiguration, Collection<Contract>> getContracts();
     }

    Example of usage in Spock tests:

    @ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
    +		.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
     		.repoRoot(StubRunnerRuleSpec.getResource("/m2repo/repository").toURI().toString())
     		.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance")
     		.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
    @@ -1905,7 +2752,8 @@ then(rule.findStubUrl(StubFinder interface and use
     its methods as presented below:

    @ContextConfiguration(classes = Config, loader = SpringBootContextLoader)
     @SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
    -		'foo=${stubrunner.runningstubs.fraudDetectionServer.port}'])
    +		'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
    +		'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
     @AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/")
     @DirtiesContext
     @ActiveProfiles("test")
    @@ -1913,6 +2761,8 @@ its methods as presented below:

    @Autowired StubFinder stubFinder
     	@Autowired Environment environment
    +	@StubRunnerPort("fraudDetectionServer") int fraudDetectionServerPort
    +	@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer") int fraudDetectionServerPortWithGroupId
     	@Value('${foo}') Integer foo
     
     	@BeforeClass
    @@ -1957,6 +2807,9 @@ its methods as presented below:

    "stubrunner.runningstubs.fraudDetectionServer.port") != null
     			stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
    +		and:
    +			environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
    +			stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
     	}
     
     	def 'should be able to interpolate a running stub in the passed test property'() {
    @@ -1965,9 +2818,20 @@ its methods as presented below:

    0
     			environment.getProperty("foo", Integer) == fraudPort
    +			environment.getProperty("fooWithGroup", Integer) == fraudPort
     			foo == fraudPort
     	}
     
    +	@Issue("#573")
    +	def 'should be able to retrieve the port of a running stub via an annotation'() {
    +		given:
    +			int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
    +		expect:
    +			fraudPort > 0
    +			fraudDetectionServerPort == fraudPort
    +			fraudDetectionServerPortWithGroupId == fraudPort
    +	}
    +
     	def 'should dump all mappings to a file'() {
     		when:
     			def url = stubFinder.findStubUrl("fraudDetectionServer")
    @@ -1983,14 +2847,21 @@ its methods as presented below:

    @AutoConfigureStubRunner.
    +    - org.springframework.cloud.contract.verifier.stubs:bootService
    +  stubs-mode: remote

    Instead of using the properties you can also use the properties inside the @AutoConfigureStubRunner. Below you can find an example of achieving the same result by setting values on the annotation.

    @AutoConfigureStubRunner(
     		ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
     		"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
     		"org.springframework.cloud.contract.verifier.stubs:bootService"],
    +		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
     		repositoryRoot = "classpath:m2repo/repository/")

    Stub Runner Spring registers environment variables in the following manner for every registered WireMock server. Example for Stub Runner ids - com.example:foo, com.example:bar.

    • stubrunner.runningstubs.foo.port
    • stubrunner.runningstubs.bar.port

    Which you can reference in your code.

    6.5 Stub Runner Spring Cloud

    Stub Runner can integrate with Spring Cloud.

    For real life examples you can check the

    6.5.1 Stubbing Service Discovery

    The most important feature of Stub Runner Spring Cloud is the fact that it’s stubbing

    • DiscoveryClient
    • Ribbon ServerList

    that means that regardless of the fact whether you’re using Zookeeper, Consul, Eureka or anything else, you don’t need that in your tests. + com.example:foo, com.example:bar.

    • stubrunner.runningstubs.foo.port
    • stubrunner.runningstubs.com.example.foo.port
    • stubrunner.runningstubs.bar.port
    • stubrunner.runningstubs.com.example.bar.port

    Which you can reference in your code.

    You can also use the @StubRunnerPort annotation to inject the port of a running stub. +Value of the annotation can be the groupid:artifactid or just the artifactid. Example for Stub Runner ids +com.example:foo, com.example:bar.

    @StubRunnerPort("foo")
    +int fooPort;
    +@StubRunnerPort("com.example:bar")
    +int barPort;

    6.5 Stub Runner Spring Cloud

    Stub Runner can integrate with Spring Cloud.

    For real life examples you can check the

    6.5.1 Stubbing Service Discovery

    The most important feature of Stub Runner Spring Cloud is the fact that it’s stubbing

    • DiscoveryClient
    • Ribbon ServerList

    that means that regardless of the fact whether you’re using Zookeeper, Consul, Eureka or anything else, you don’t need that in your tests. We’re starting WireMock instances of your dependencies and we’re telling your application whenever you’re using Feign, load balanced RestTemplate or DiscoveryClient directly, to call those stubbed servers instead of calling the real Service Discovery tool.

    For example this test will pass

    def 'should make service discovery work'() {
     	expect: 'WireMocks are running'
    @@ -2014,15 +2885,17 @@ You can disable Stub Runner Ribbon support by providing: s
     You can disable Stub Runner support by providing: stubrunner.cloud.enabled equal to false

    [Tip]Tip

    By default all service discovery will be stubbed. That means that regardless of the fact if you have an existing DiscoveryClient its results will be ignored. However, if you want to reuse it, just set stubrunner.cloud.delegate.enabled to true and then your existing DiscoveryClient results will be - merged with the stubbed ones.

    6.6 Stub Runner Boot Application

    Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to + merged with the stubbed ones.

    The default Maven configuration used by Stub Runner can be tweaked either +via the following system properties or environment variables

    • maven.repo.local - path to the custom maven local repository location
    • org.apache.maven.user-settings - path to custom maven user settings location
    • org.apache.maven.global-settings - path to maven global settings location

    6.6 Stub Runner Boot Application

    Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to trigger the messaging labels and to access started WireMock servers.

    One of the use-cases is to run some smoke (end to end) tests on a deployed application. You can check out the Spring Cloud Pipelines -project for more information.

    6.6.1 How to use it?

    Stub Runner Server

    Just add the

    compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

    Annotate a class with @EnableStubRunnerServer, build a fat-jar and you’re ready to go!

    For the properties check the Stub Runner Spring section.

    Spring Cloud CLI

    Starting from 1.4.0.RELEASE version of the Spring Cloud CLI +project for more information.

    6.6.1 How to use it?

    Stub Runner Server

    Just add the

    compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

    Annotate a class with @EnableStubRunnerServer, build a fat-jar and you’re ready to go!

    For the properties check the Stub Runner Spring section.

    Stub Runner Server Fat Jar

    You can download a standalone JAR from Maven (for example, for version 1.2.3.RELEASE), as follows:

    $ wget -O stub-runner.jar 'https://search.maven.org/remote_content?g=org.springframework.cloud&a=spring-cloud-contract-stub-runner-boot&v=1.2.3.RELEASE'
    +$ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...

    Spring Cloud CLI

    Starting from 1.4.0.RELEASE version of the Spring Cloud CLI project you can start Stub Runner Boot by executing spring cloud stubrunner.

    In order to pass the configuration just create a stubrunner.yml file in the current working directory or a subdirectory called config or in ~/.spring-cloud. The file could look like this (example for running stubs installed locally)

    stubrunner.yml. 

    stubrunner:
    -  workOffline: true
    +  stubsMode: LOCAL
       ids:
         - com.example:beer-api-producer:+:9876

    and then just call spring cloud stubrunner from your terminal window to start @@ -2153,7 +3026,7 @@ There are 2 consumers: foo-consumer and 200 + status OK() body( foo: "foo" } @@ -2162,7 +3035,7 @@ response { method GET() } response { - status 200 + status OK() body( bar: "bar" } @@ -2182,6 +3055,7 @@ Or set the test as follows:

    @SpringBootTest(properties = ["spring.application.name=bar-consumer"])
     @AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
     		repositoryRoot = "classpath:m2repo/repository/",
    +		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
     		stubsPerConsumer = true)
     @DirtiesContext
     class StubRunnerStubsPerConsumerSpec extends Specification {
    @@ -2192,6 +3066,7 @@ Or set the test as follows:

    @AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
     		repositoryRoot = "classpath:m2repo/repository/",
     		consumerName = "foo-consumer",
    +		stubsMode = StubRunnerProperties.StubsMode.REMOTE,
     		stubsPerConsumer = true)
     @DirtiesContext
     class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
    @@ -2199,8 +3074,7 @@ Or set the test as follows:

    foo-consumer in its name (i.e. those from the
     src/test/resources/contracts/foo-consumer/some/contracts/…​ folder) will be allowed to be referenced.

    You can check out issue 224 for more information about the reasons behind this change.

    6.8 Common

    This section briefly describes common properties, including:

    6.8.1 Common Properties for JUnit and Spring

    You can set repetitive properties by using system properties or Spring configuration -properties. Here are their names with their default values:

    Property nameDefault valueDescription

    stubrunner.minPort

    10000

    Minimum value of a port for a started WireMock with stubs.

    stubrunner.maxPort

    15000

    Maximum value of a port for a started WireMock with stubs.

    stubrunner.repositoryRoot

     

    Maven repo URL. If blank, then call the local maven repo.

    stubrunner.classifier

    stubs

    Default classifier for the stub artifacts.

    stubrunner.workOffline

    false

    If true, then do not contact any remote repositories to -download stubs.

    stubrunner.ids

     

    Array of Ivy notation stubs to download.

    stubrunner.username

     

    Optional username to access the tool that stores the JARs with +properties. Here are their names with their default values:

    Property nameDefault valueDescription

    stubrunner.minPort

    10000

    Minimum value of a port for a started WireMock with stubs.

    stubrunner.maxPort

    15000

    Maximum value of a port for a started WireMock with stubs.

    stubrunner.repositoryRoot

     

    Maven repo URL. If blank, then call the local maven repo.

    stubrunner.classifier

    stubs

    Default classifier for the stub artifacts.

    stubrunner.stubsMode

    CLASSPATH

    The way you want to fetch and register the stubs

    stubrunner.ids

     

    Array of Ivy notation stubs to download.

    stubrunner.username

     

    Optional username to access the tool that stores the JARs with stubs.

    stubrunner.password

     

    Optional password to access the tool that stores the JARs with stubs.

    stubrunner.stubsPerConsumer

    false

    Set to true if you want to use different stubs for each consumer instead of registering all stubs for every consumer.

    stubrunner.consumerName

     

    If you want to use a stub for each consumer and want to @@ -2210,7 +3084,34 @@ pass an empty classifier this way: groupId:artifactId:vers downloaded.

    port means the port of the WireMock server.

    [Important]Important

    Starting with version 1.0.4, you can provide a range of versions that you would like the Stub Runner to take into consideration. You can read more about the Aether versioning -ranges here.

    7. Stub Runner for Messaging

    Stub Runner can run the published stubs in memory. It can integrate with the following +ranges here.

    6.9 Stub Runner Docker

    We’re publishing a spring-cloud/spring-cloud-contract-stub-runner Docker image +that will start the standalone version of Stub Runner.

    If you want to learn more about the basics of Maven, artifact ids, +group ids, classifiers and Artifact Managers, just click here Section 4.6, “Docker Project”.

    6.9.1 How to use it

    Just execute the docker image. You can pass any of the Section 6.8.1, “Common Properties for JUnit and Spring” +as environment variables. The convention is that all the +letters should be upper case. The camel case notation should +and the dot (.) should be separated via underscore (_). E.g. + the stubrunner.repositoryRoot property should be represented + as a STUBRUNNER_REPOSITORY_ROOT environment variable.

    6.9.2 Example of client side usage in a non JVM project

    We’d like to use the stubs created in this Section 4.6.4, “Server side (nodejs)” step. +Let’s assume that we want to run the stubs on port 9876. The NodeJS code +is available here:

    $ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
    +$ cd bookstore

    Let’s run the Stub Runner Boot application with the stubs.

    # Provide the Spring Cloud Contract Docker version
    +$ SC_CONTRACT_DOCKER_VERSION="..."
    +# The IP at which the app is running and Docker container can reach it
    +$ APP_IP="192.168.0.100"
    +# Spring Cloud Contract Stub Runner properties
    +$ STUBRUNNER_PORT="8083"
    +# Stub coordinates 'groupId:artifactId:version:classifier:port'
    +$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
    +$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
    +# Run the docker with Stub Runner Boot
    +$ docker run  --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"

    What’s happening is that

    • a standalone Stub Runner application got started
    • it downloaded the stub with coordinates com.example:bookstore:0.0.1.RELEASE:stubs on port 9876
    • it got downloaded from Artifactory running at http://192.168.0.100:8081/artifactory/libs-release-local
    • after a while Stub Runner will be running on port 8083
    • and the stubs will be running at port 9876

    On the server side we built a stateful stub. Let’s use curl to assert +that the stubs are setup properly.

    # let's execute the first request (no response is returned)
    +$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
    +# Now time for the second request
    +$ curl -X GET http://localhost:9876/api/books
    +# You will receive contents of the JSON
    [Important]Important

    If you want use the stubs that you have built locally, on your host, +then you should pass the environment variable -e STUBRUNNER_STUBS_MODE=LOCAL and mount +the volume of your local m2 -v "${HOME}/.m2/:/root/.m2:ro"

    7. Stub Runner for Messaging

    Stub Runner can run the published stubs in memory. It can integrate with the following frameworks:

    • Spring Integration
    • Spring Cloud Stream
    • Spring AMQP

    It also provides entry points to integrate with any other solution on the market.

    [Important]Important

    If you have multiple frameworks on the classpath Stub Runner will need to define which one should be used. Let’s assume that you have both AMQP, Spring Cloud Stream and Spring Integration on the classpath. Then you need to set stubrunner.stream.enabled=false and stubrunner.integration.enabled=false. @@ -2394,7 +3295,7 @@ property.

    Assume that you have the following Maven repository with a deplo } }

    Now consider the following Spring configuration:

    stubrunner.repositoryRoot: classpath:m2repo/repository/
     stubrunner.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs
    -
    +stubrunner.stubs-mode: remote
     spring:
       cloud:
         stream:
    @@ -2471,6 +3372,7 @@ to disable them explicitly by setting the  stubrunner.stre
     }

    Now consider the following Spring configuration:

    stubrunner:
       repositoryRoot: classpath:m2repo/repository/
       ids: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:0.4.0-SNAPSHOT:stubs
    +  stubs-mode: remote
       amqp:
         enabled: true
     server:
    @@ -2499,16 +3401,15 @@ definition is matched and invoked with the contract message.

    ConnectionFactory.

    To disable the mocked ConnectionFactory, set the following property: stubrunner.amqp.mockConnection=false

    stubrunner:
       amqp:
    -    mockConnection: false

    8. Contract DSL

    [Important]Important

    Remember that, inside the contract file, you have to provide the fully + mockConnection: false

    8. Contract DSL

    Spring Cloud Contract supports out of the box 2 types of DSL. One written in +Groovy and one written in YAML.

    If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy +before. Knowledge of the language is not really needed, as the Contract DSL uses only a +tiny subset of it (only literals, method calls and closures). Also, the DSL is statically +typed, to make it programmer-readable without any knowledge of the DSL itself.

    [Important]Important

    Remember that, inside the Groovy contract file, you have to provide the fully qualified name to the Contract class and make static imports, such as org.springframework.cloud.spec.Contract.make { …​ }. You can also provide an import to the Contract class: import org.springframework.cloud.spec.Contract and then call -Contract.make { …​ }.

    Contract DSL is written in Groovy, but do not be alarmed if you have not used Groovy -before. Knowledge of the language is not really needed, as the Contract DSL uses only a -tiny subset of it (only literals, method calls and closures). Also, the DSL is statically -typed, to make it programmer-readable without any knowledge of the DSL itself.

    [Tip]Tip

    Spring Cloud Contract supports defining multiple contracts in a single file.

    The Contract is present in the spring-cloud-contract-spec module of the -Spring -Cloud Contract Verifier repository.

    The following is a complete example of a contract definition:

    org.springframework.cloud.contract.spec.Contract.make {
    +Contract.make { …​ }.

    [Tip]Tip

    Spring Cloud Contract supports defining multiple contracts in a single file.

    The following is a complete example of a Groovy contract definition:

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		method 'PUT'
     		url '/api/12'
    @@ -2547,18 +3448,66 @@ Cloud Contract Verifier repository.

    The following is a complete exampl ''' } response { - status 200 + status OK() } -}

    [Note]Note

    The preceding example does not contain all the features of the DSL appear. The -remainder of this section describes the other features.

    You can compile Contracts to WireMock stubs mapping using standalone maven command: -mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert

    8.1 Limitations

    [Warning]Warning

    Spring Cloud Contract Verifier does not properly support XML. Please use JSON or +}

    The following is a complete example of a YAML contract definition:

    description: Some description
    +name: some name
    +priority: 8
    +ignored: true
    +request:
    +  url: /foo
    +  queryParameters:
    +    a: b
    +    b: c
    +  method: PUT
    +  headers:
    +    foo: bar
    +    fooReq: baz
    +  body:
    +    foo: bar
    +  matchers:
    +    body:
    +      - path: $.foo
    +        type: by_regex
    +        value: bar
    +    headers:
    +      - key: foo
    +        regex: bar
    +response:
    +  status: 200
    +  headers:
    +    foo2: bar
    +    foo3: foo33
    +    fooRes: baz
    +  body:
    +    foo2: bar
    +    foo3: baz
    +    nullValue: null
    +  matchers:
    +    body:
    +      - path: $.foo2
    +        type: by_regex
    +        value: bar
    +      - path: $.foo3
    +        type: by_command
    +        value: executeMe($it)
    +      - path: $.nullValue
    +        type: by_null
    +        value: null
    +    headers:
    +      - key: foo2
    +        regex: bar
    +      - key: foo3
    +        command: andMeToo($it)
    [Tip]Tip

    You can compile contracts to stubs mapping using standalone maven command: +mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert

    8.1 Limitations

    [Warning]Warning

    Spring Cloud Contract Verifier does not properly support XML. Please use JSON or help us implement this feature.

    [Warning]Warning

    The support for verifying the size of JSON arrays is experimental. If you want to turn it on, please set the value of the following system property to true: spring.cloud.contract.verifier.assert.size. By default, this feature is set to false. You can also provide the assertJsonSize property in the plugin configuration.

    [Warning]Warning

    Because JSON structure can have any form, it can be impossible to parse it -properly when using the value(consumer(…​), producer(…​)) notation in GString. That +properly when using the Groovy DSL and the value(consumer(…​), producer(…​)) notation in GString. That is why you should use the Groovy Map notation.

    8.2 Common Top-Level elements

    The following sections describe the most common top-level elements:

    8.2.1 Description

    You can add a description to your contract. The description is arbitrary text. The -following code shows an example:

    		org.springframework.cloud.contract.spec.Contract.make {
    +following code shows an example:

    Groovy DSL.  +

    		org.springframework.cloud.contract.spec.Contract.make {
     			description('''
     given:
     	An input
    @@ -2567,23 +3516,85 @@ when:
     then:
     	Output
     ''')
    -		}

    8.2.2 Name

    You can provide a name for your contract. Assume that you provided the following name: + }

    +

    YAML.  +

    description: Some description
    +name: some name
    +priority: 8
    +ignored: true
    +request:
    +  url: /foo
    +  queryParameters:
    +    a: b
    +    b: c
    +  method: PUT
    +  headers:
    +    foo: bar
    +    fooReq: baz
    +  body:
    +    foo: bar
    +  matchers:
    +    body:
    +      - path: $.foo
    +        type: by_regex
    +        value: bar
    +    headers:
    +      - key: foo
    +        regex: bar
    +response:
    +  status: 200
    +  headers:
    +    foo2: bar
    +    foo3: foo33
    +    fooRes: baz
    +  body:
    +    foo2: bar
    +    foo3: baz
    +    nullValue: null
    +  matchers:
    +    body:
    +      - path: $.foo2
    +        type: by_regex
    +        value: bar
    +      - path: $.foo3
    +        type: by_command
    +        value: executeMe($it)
    +      - path: $.nullValue
    +        type: by_null
    +        value: null
    +    headers:
    +      - key: foo2
    +        regex: bar
    +      - key: foo3
    +        command: andMeToo($it)

    +

    8.2.2 Name

    You can provide a name for your contract. Assume that you provided the following name: should register a user. If you do so, the name of the autogenerated test is validate_should_register_a_user. Also, the name of the stub in a WireMock stub is should_register_a_user.json.

    [Important]Important

    You must ensure that the name does not contain any characters that make the generated test not compile. Also, remember that, if you provide the same name for multiple contracts, your autogenerated tests fail to compile and your generated stubs -override each other.

    8.2.3 Ignoring Contracts

    If you want to ignore a contract, you can either set a value of ignored contracts in the -plugin configuration or set the ignored property on the contract itself:

    org.springframework.cloud.contract.spec.Contract.make {
    +override each other.

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
    +	name("some_special_name")
    +}

    +

    YAML.  +

    name: some name

    +

    8.2.3 Ignoring Contracts

    If you want to ignore a contract, you can either set a value of ignored contracts in the +plugin configuration or set the ignored property on the contract itself:

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	ignored()
    -}

    8.2.4 Passing Values from Files

    Starting with version 1.2.0, you can pass values from files. Assume that you have the +}

    +

    YAML.  +

    ignored: true

    +

    8.2.4 Passing Values from Files

    Starting with version 1.2.0, you can pass values from files. Assume that you have the following resources in our project.

    └── src
         └── test
             └── resources
                 └── contracts
                     ├── readFromFile.groovy
                     ├── request.json
    -                └── response.json

    Further assume that your contract is as follows:

    import org.springframework.cloud.contract.spec.Contract
    +                └── response.json

    Further assume that your contract is as follows:

    Groovy DSL.  +

    import org.springframework.cloud.contract.spec.Contract
     
     Contract.make {
     	request {
    @@ -2595,17 +3606,26 @@ Contract.make {
     		url("/1")
     	}
     	response {
    -		status 200
    +		status OK()
     		body(file("response.json"))
     		headers {
     			contentType(textPlain())
     		}
     	}
    -}

    Further assume that the JSON files is as follows:

    request.json

    { "status" : "REQUEST" }

    response.json

    { "status" : "RESPONSE" }

    When test or stub generation takes place, the contents of the file is passed to the body -of a request or a response. That works because of the file(…​) method. The argument of -that method needs to be a file with location relative to the folder in which the contract -lays.

    8.2.5 HTTP Top-Level Elements

    The following methods can be called in the top-level closure of a contract definition. -request and response are mandatory. priority is optional.

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    request:
    +  method: GET
    +  url: /foo
    +  bodyFromFile: request.json
    +response:
    +  status: 200
    +  bodyFromFile: response.json

    +

    Further assume that the JSON files is as follows:

    request.json

    { "status" : "REQUEST" }

    response.json

    { "status" : "RESPONSE" }

    When test or stub generation takes place, the contents of the file is passed to the body +of a request or a response. The name of the file needs to be a file with location +relative to the folder in which the contract lays.

    8.2.5 HTTP Top-Level Elements

    The following methods can be called in the top-level closure of a contract definition. +request and response are mandatory. priority is optional.

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	// Definition of HTTP request part of the contract
     	// (this can be a valid request or invalid depending
     	// on type of contract being specified).
    @@ -2624,8 +3644,18 @@ lays.

    // Contract priority, which can be used for overriding // contracts (1 is highest). Priority is optional. priority 1 -}

    8.3 Request

    The HTTP protocol requires only method and address to be specified in a request. The -same information is mandatory in request definition of the Contract.

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    priority: 8
    +request:
    +...
    +response:
    +...

    +

    [Important]Important

    If you want to make your contract have a higher value of priority +you need to pass a lower number to the priority tag / method. E.g. priority with +value 5 has higher priority than priority with value 10.

    8.3 Request

    The HTTP protocol requires only method and url to be specified in a request. The +same information is mandatory in request definition of the Contract.

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		// HTTP request method (GET/POST/PUT/DELETE).
     		method 'GET'
    @@ -2637,8 +3667,13 @@ same information is mandatory in request definition of the Contract.

    //...
     	}
    -}

    It is possible to specify an absolute rather than relative url, but using urlPath is -the recommended way, as doing so makes the tests host-independent.

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    method: PUT
    +url: /foo

    +

    It is possible to specify an absolute rather than relative url, but using urlPath is +the recommended way, as doing so makes the tests host-independent.

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		method 'GET'
     
    @@ -2649,8 +3684,13 @@ the recommended way, as doing so makes the tests ho
     	response {
     		//...
     	}
    -}

    request may contain query parameters, which are specified in a closure nested in a -call to urlPath or url.

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    request:
    +  method: PUT
    +  urlPath: /foo

    +

    request may contain query parameters.

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		//...
     
    @@ -2690,7 +3730,62 @@ call to urlPath or url
     	response {
     		//...
     	}
    -}

    request may contain additional request headers, as shown in the following example:

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    request:
    +...
    +  queryParameters:
    +    a: b
    +    b: c
    +  headers:
    +    foo: bar
    +    fooReq: baz
    +  cookies:
    +    foo: bar
    +    fooReq: baz
    +  body:
    +    foo: bar
    +  matchers:
    +    body:
    +      - path: $.foo
    +        type: by_regex
    +        value: bar
    +    headers:
    +      - key: foo
    +        regex: bar
    +response:
    +  status: 200
    +  headers:
    +    foo2: bar
    +    foo3: foo33
    +    fooRes: baz
    +  body:
    +    foo2: bar
    +    foo3: baz
    +    nullValue: null
    +  matchers:
    +    body:
    +      - path: $.foo2
    +        type: by_regex
    +        value: bar
    +      - path: $.foo3
    +        type: by_command
    +        value: executeMe($it)
    +      - path: $.nullValue
    +        type: by_null
    +        value: null
    +    headers:
    +      - key: foo2
    +        regex: bar
    +      - key: foo3
    +        command: andMeToo($it)
    +    cookies:
    +      - key: foo2
    +        regex: bar
    +      - key: foo3
    +        predefined:

    +

    request may contain additional request headers, as shown in the following example:

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		//...
     
    @@ -2707,7 +3802,40 @@ call to urlPath or url
     	response {
     		//...
     	}
    -}

    request may contain a request body, as shown in the following example:

    org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    request:
    +...
    +headers:
    +  foo: bar
    +  fooReq: baz

    +

    request may contain additional request cookies, as shown in the following example:

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
    +	request {
    +		//...
    +
    +		// Each Cookies is added in form `'Cookie-Key' : 'Cookie-Value'`.
    +		// there are also some helper methods
    +		cookies {
    +			cookie 'key': 'value'
    +			cookie('another_key', 'another_value')
    +		}
    +
    +		//...
    +	}
    +
    +	response {
    +		//...
    +	}
    +}

    +

    YAML.  +

    request:
    +...
    +cookies:
    +  foo: bar
    +  fooReq: baz

    +

    request may contain a request body:

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		//...
     
    @@ -2719,8 +3847,15 @@ call to urlPath or url
     	response {
     		//...
     	}
    -}

    request may contain multipart elements. To include multipart elements, call the -multipart() method, as shown in the following example

    org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
    +}

    +

    YAML.  +

    request:
    +...
    +body:
    +  foo: bar

    +

    request may contain multipart elements. To include multipart elements, use the +multipart method/section, as shown in the following examples

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		method "PUT"
     		url "/multipart"
    @@ -2737,17 +3872,75 @@ call to urlPath or url
     						// name of the file
     						name: $(c(regex(nonEmpty())), p('filename.csv')),
     						// content of the file
    -						content: $(c(regex(nonEmpty())), p('file content')))
    +						content: $(c(regex(nonEmpty())), p('file content')),
    +						// content type for the part
    +						contentType: $(c(regex(nonEmpty())), p('application/json')))
    +		)
    +	}
    +	response {
    +		status OK()
    +	}
    +}
    +org.springframework.cloud.contract.spec.Contract contractDsl = org.springframework.cloud.contract.spec.Contract.make {
    +	request {
    +		method "PUT"
    +		url "/multipart"
    +		headers {
    +			contentType('multipart/form-data;boundary=AaB03x')
    +		}
    +		multipart(
    +				file: named(
    +						name: value(stub(regex('.+')), test('file')),
    +						content: value(stub(regex('.+')), test([100, 117, 100, 97] as byte[]))
    +				)
     		)
     	}
     	response {
     		status 200
     	}
    -}

    In the preceding example, we define parameters in either of two ways:

    • Directly, by using the map notation, where the value can be a dynamic property (such as +}

    +

    YAML.  +

    request:
    +  method: PUT
    +  url: /multipart
    +  headers:
    +    Content-Type: multipart/form-data;boundary=AaB03x
    +  multipart:
    +    params:
    +    # key (parameter name), value (parameter value) pair
    +      formParameter: '"formParameterValue"'
    +      someBooleanParameter: true
    +    named:
    +      - paramName: file
    +        fileName: filename.csv
    +        fileContent: file content
    +  matchers:
    +    multipart:
    +      params:
    +        - key: formParameter
    +          regex: ".+"
    +        - key: someBooleanParameter
    +          predefined: any_boolean
    +      named:
    +        - paramName: file
    +          fileName:
    +            predefined: non_empty
    +          fileContent:
    +            predefined: non_empty
    +response:
    +  status: 200

    +

    In the preceding example, we define parameters in either of two ways:

    Groovy DSL

    • Directly, by using the map notation, where the value can be a dynamic property (such as formParameter: $(consumer(…​), producer(…​))).
    • By using the named(…​) method that lets you set a named parameter. A named parameter can set a name and content. You can call it either via a method with two arguments, such as named("fileName", "fileContent"), or via a map notation, such as -named(name: "fileName", content: "fileContent").

    From this contract, the generated test is as follows:

    // given:
    +named(name: "fileName", content: "fileContent").

    YAML

    • The multipart parameters are set via multipart.params section
    • The named parameters (the fileName and fileContent for a given parameter name) +can be set via the multipart.named section. That section contains +the paramName (name of the parameter), fileName (name of the file), +fileContent (content of the file) fields
    • The dynamic bits can be set via the matchers.multipart section

      • for parameters use the params section that can accept +regex or a predefined regular expression
      • for named params use the named section where first you +define the parameter name via paramName and then you can pass the +parametrization of either fileName or fileContent via +regex or a predefined regular expression

    From this contract, the generated test is as follows:

    // given:
      MockMvcRequestSpecification request = given()
        .header("Content-Type", "multipart/form-data;boundary=AaB03x")
        .param("formParameter", "\"formParameterValue\"")
    @@ -2770,11 +3963,11 @@ such as named("fileName", "fileContent"), or via a
     	  }
     	},
     	"bodyPatterns" : [ {
    -		"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"formParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\".+\\"\\r\\n--\\\\1.*"
    +		"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"formParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\".+\\"\\r\\n--\\\\1.*"
       		}, {
    -    			"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"someBooleanParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"
    +    			"matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"someBooleanParameter\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"
       		}, {
    -	  "matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"file\\"; filename=\\"[\\\\S\\\\s]+\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n[\\\\S\\\\s]+\\r\\n--\\\\1.*"
    +	  "matches" : ".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"file\\"; filename=\\"[\\\\S\\\\s]+\\"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n[\\\\S\\\\s]+\\r\\n--\\\\1.*"
     	} ]
       },
       "response" : {
    @@ -2783,21 +3976,31 @@ such as named("fileName", "fileContent"), or via a
       }
     }
     	'''

    8.4 Response

    The response must contain an HTTP status code and may contain other information. The -following code shows an example:

    org.springframework.cloud.contract.spec.Contract.make {
    +following code shows an example:

    Groovy DSL.  +

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
     		//...
     	}
     	response {
     		// Status code sent by the server
     		// in response to request specified above.
    -		status 200
    +		status OK()
     	}
    -}

    Besides status, the response may contain headers and a body, both of which are -specified the same way as in the request (see the previous paragraph).

    8.5 Dynamic properties

    The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not +}

    +

    YAML.  +

    response:
    +...
    +status: 200

    +

    Besides status, the response may contain headers, cookies and a body, both of which are +specified the same way as in the request (see the previous paragraph).

    [Tip]Tip

    Via the Groovy DSL you can reference the org.springframework.cloud.contract.spec.internal.HttpStatus +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. 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.

    8.5.1 Dynamic properties inside the body

    You can set the properties inside the body either with the value method or, if you use +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 a separate section called +bodyMatchers.

    [Note]Note

    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.

    8.5.1 Dynamic properties inside the body

    [Important]Important

    This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

    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(...))
     value(c(...), p(...))
    @@ -2806,7 +4009,8 @@ value(client(...), server(...))

    The following example shows how to set d $(c(...), p(...)) $(stub(...), test(...)) $(client(...), server(...))

    Both approaches work equally well. stub and client methods are aliases over the consumer -method. Subsequent sections take a closer look at what you can do with those values.

    8.5.2 Regular expressions

    You can use regular expressions to write your requests in Contract DSL. Doing so is +method. Subsequent sections take a closer look at what you can do with those values.

    8.5.2 Regular expressions

    [Important]Important

    This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

    You can use regular expressions to write your requests in Contract DSL. Doing so is particularly useful when you want to indicate that a given response should be provided for requests that follow a given pattern. Also, you can use regular expressions when you need to use patterns and not exact values both for your test and your server side tests.

    The following example shows how to use regular expressions to write a request:

    org.springframework.cloud.contract.spec.Contract.make {
    @@ -2815,7 +4019,7 @@ need to use patterns and not exact values both for your test and your server sid
     		url $(consumer(~/\/[0-9]{2}/), producer('/12'))
     	}
     	response {
    -		status 200
    +		status OK()
     		body(
     				id: $(anyNumber()),
     				surname: $(
    @@ -2846,7 +4050,7 @@ the provided regular expression. The following code shows an example:

    200
    +		status OK()
     		body([
     			responseElement: $(producer(regex('[0-9]{7}')))
     		])
    @@ -2857,12 +4061,18 @@ 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:

    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+)?')
    +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}')
     protected static final Pattern URL = UrlHelper.URL
    +protected static final Pattern HTTPS_URL = UrlHelper.HTTPS_URL
     protected static final Pattern UUID = Pattern.compile('[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}')
     protected static final Pattern ANY_DATE = Pattern.compile('(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])')
     protected static final Pattern ANY_DATE_TIME = Pattern.compile('([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])')
    @@ -2879,14 +4089,30 @@ Pattern onlyAlphaUnicode() {
     	return ONLY_ALPHA_UNICODE
     }
     
    +Pattern alphaNumeric() {
    +	return ALPHA_NUMERIC
    +}
    +
     Pattern number() {
     	return NUMBER
     }
     
    +Pattern positiveInt() {
    +	return POSITIVE_INT
    +}
    +
     Pattern anyBoolean() {
     	return TRUE_OR_FALSE
     }
     
    +Pattern anInteger() {
    +	return INTEGER
    +}
    +
    +Pattern aDouble() {
    +	return DOUBLE
    +}
    +
     Pattern ipAddress() {
     	return IP_ADDRESS
     }
    @@ -2903,6 +4129,10 @@ Pattern url() {
     	return URL
     }
     
    +Pattern httpsUrl() {
    +	return HTTPS_URL
    +}
    +
     Pattern uuid(){
     	return UUID
     }
    @@ -2952,7 +4182,8 @@ Pattern nonBlank() {
     				message: "User not found by email = [${value(producer(regex(email())), consumer('not.existing@user.com'))}]"
     		)
     	}
    -}

    8.5.3 Passing Optional Parameters

    It is possible to provide optional parameters in your contract. However, you can provide +}

    8.5.3 Passing Optional Parameters

    [Important]Important

    This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

    It is possible to provide optional parameters in your contract. However, you can provide optional parameters only for the following:

    • STUB side of the Request
    • TEST side of the Response

    The following example shows how to provide optional parameters:

    org.springframework.cloud.contract.spec.Contract.make {
     	priority 1
     	request {
    @@ -2995,29 +4226,30 @@ expression that must be present 0 or more times.

    If you use Spock for, the """

    The following stub would also be generated:

    '''
     {
       "request" : {
    -    "url" : "/users/password",
    -    "method" : "POST",
    -    "bodyPatterns" : [ {
    -      "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
    -    } ],
    -    "headers" : {
    -      "Content-Type" : {
    -        "equalTo" : "application/json"
    -      }
    -    }
    +	"url" : "/users/password",
    +	"method" : "POST",
    +	"bodyPatterns" : [ {
    +	  "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
    +	} ],
    +	"headers" : {
    +	  "Content-Type" : {
    +		"equalTo" : "application/json"
    +	  }
    +	}
       },
       "response" : {
    -    "status" : 404,
    -    "body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}",
    -    "headers" : {
    -      "Content-Type" : "application/json"
    -    }
    +	"status" : 404,
    +	"body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}",
    +	"headers" : {
    +	  "Content-Type" : "application/json"
    +	}
       },
       "priority" : 1
     }
    -'''

    8.5.4 Executing Custom Methods on the Server Side

    You can define a method call that executes on the server side during the test. Such a +'''

    8.5.4 Executing Custom Methods on the Server Side

    [Important]Important

    This section is valid only for Groovy DSL. Check out the +Section 8.5.7, “Dynamic Properties in the Matchers Sections” section for YAML examples of a similar feature.

    You can define a method call that executes on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" in the configuration. The following code shows an example of the contract portion of the test case:

    org.springframework.cloud.contract.spec.Contract.make {
     	request {
    @@ -3037,7 +4269,7 @@ following code shows an example of the contract portion of the test case:

    '/api/12'), producer(regex('^/api/[0-9]{2}$'))), correlationId: $(consumer('1223456'), producer(execute('isProperCorrelationId($it)'))) ) - status 200 + status OK() } }

    The following code shows the base class portion of the test case:

    abstract class BaseMockMvcSpec extends Specification {
     
    @@ -3068,7 +4300,7 @@ is applied for the whole body - not for parts of it.

    ) } response { - status 200 + status OK() } }

    The preceding example results in calling the hashCode() method in the request body. It should resemble the following code:

    // given:
    @@ -3081,11 +4313,17 @@ It should resemble the following code:

    // then:
      assertThat(response.statusCode()).isEqualTo(200);

    8.5.5 Referencing the Request from the Response

    The best situation is to provide fixed values, but sometimes you need to reference a -request in your response. To do so, you can use the fromRequest() method, which lets +request in your response.

    If you’re writing contracts using Groovy DSL, you can use the fromRequest() method, which lets you reference a bunch of elements from the HTTP request. You can use the following options:

    • fromRequest().url(): Returns the request URL and query parameters.
    • fromRequest().query(String key): Returns the first query parameter with a given name.
    • fromRequest().query(String key, int index): Returns the nth query parameter with a given name.
    • fromRequest().path(): Returns the full path.
    • fromRequest().path(int index): Returns the nth path element.
    • fromRequest().header(String key): Returns the first header with a given name.
    • fromRequest().header(String key, int index): Returns the nth header with a given name.
    • fromRequest().body(): Returns the full request body.
    • fromRequest().body(String jsonPath): Returns the element from the request that -matches the JSON Path.

    Consider the following contract:

    Contract contractDsl = Contract.make {
    +matches the JSON Path.

    If you’re using the YAML contract definition you have to use the +Handlebars {{{ }}} notation with custom, Spring Cloud Contract + functions to achieve this.

    • {{{ request.url }}}: Returns the request URL and query parameters.
    • {{{ request.query.key.[index] }}}: Returns the nth query parameter with a given name. +E.g. for key foo, first entry {{{ request.query.foo.[0] }}}
    • {{{ request.path }}}: Returns the full path.
    • {{{ request.path.[index] }}}: Returns the nth path element. E.g. +for first entry `{{{ request.path.[0] }}}
    • {{{ request.headers.key }}}: Returns the first header with a given name.
    • {{{ request.headers.key.[index] }}}: Returns the nth header with a given name.
    • {{{ request.body }}}: Returns the full request body.
    • {{{ jsonpath this 'your.json.path' }}}: Returns the element from the request that +matches the JSON Path. E.g. for json path $.foo - {{{ jsonpath this '$.foo' }}}

    Consider the following contract:

    Groovy DSL.  +

    Contract contractDsl = Contract.make {
     	request {
     		method 'GET'
     		url('/api/v1/xxxx') {
    @@ -3101,7 +4339,7 @@ matches the JSON Path.

    Consider the following contract:

    "bar", baz: 5) } response { - status 200 + status OK() headers { header(authorization(), "foo ${fromRequest().header(authorization())} bar") } @@ -3119,7 +4357,39 @@ matches the JSON Path.

    Consider the following contract:

    "Bla bla ${fromRequest().body('$.foo')} bla bla" ) } -}

    Running a JUnit test generation leads to a test that resembles the following example:

    // given:
    +}

    +

    YAML.  +

    request:
    +  method: GET
    +  url: /api/v1/xxxx
    +  queryParameters:
    +    foo:
    +      - bar
    +      - bar2
    +  headers:
    +    Authorization:
    +      - secret
    +      - secret2
    +  body:
    +    foo: bar
    +    baz: 5
    +response:
    +  status: 200
    +  headers:
    +    Authorization: "foo {{{ request.headers.Authorization.0 }}} bar"
    +  body:
    +    url: "{{{ request.url }}}"
    +    path: "{{{ request.path }}}"
    +    pathIndex: "{{{ request.path.1 }}}"
    +    param: "{{{ request.query.foo }}}"
    +    paramIndex: "{{{ request.query.foo.1 }}}"
    +    authorization: "{{{ request.headers.Authorization.0 }}}"
    +    authorization2: "{{{ request.headers.Authorization.1 }}"
    +    fullBody: "{{{ request.body }}}"
    +    responseFoo: "{{{ jsonpath this '$.foo' }}}"
    +    responseBaz: "{{{ jsonpath this '$.baz' }}}"
    +    responseBaz2: "Bla bla {{{ jsonpath this '$.foo' }}} bla bla"

    +

    Running a JUnit test generation leads to a test that resembles the following example:

    // given:
      MockMvcRequestSpecification request = given()
        .header("Authorization", "secret")
        .header("Authorization", "secret2")
    @@ -3196,7 +4466,9 @@ provide your own extensions, you can register an implementation of the
     org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions interface.
     Since we use the spring.factories extension approach, you can create an entry in
     META-INF/spring.factories file similar to the following:

    org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\
    -org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions

    The following is an example of a custom extension:

    TestWireMockExtensions.groovy.  +org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions +org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.stubrunner.TestCustomYamlContractConverter

    The following is an example of a custom extension:

    TestWireMockExtensions.groovy. 

    package org.springframework.cloud.contract.verifier.dsl.wiremock
     
     import com.github.tomakehurst.wiremock.extension.Extension
    @@ -3222,30 +4494,39 @@ org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExte
     	}
     }

    [Important]Important

    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. +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:

      • For stubMatchers:

        • 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 -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 -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.
        • byType(): The value taken from the response via the provided JSON Path needs to be +following matching possibilities:

          Groovy DSL

          • For the stubs(in tests on the Consumer’s side):

            • byEquality(): The value taken from the consumer’s request via the provided JSON Path must be +equal to the value provided in the contract.
            • byRegex(…​): The value taken from the consumer’s request via the provided JSON Path must +match the regex.
            • byDate(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO Date value.
            • byTimestamp(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO DateTime value.
            • byTime(): The value taken from the consumer’s request via the provided JSON Path must +match the regex for an ISO Time value.
          • For the verification(in generated tests on the Producer’s side):

            • byEquality(): The value taken from the producer’s response via the provided JSON Path must be +equal to the provided value in the contract.
            • byRegex(…​): The value taken from the producer’s response via the provided JSON Path must +match the regex.
            • byDate(): The value taken from the producer’s response via the provided JSON Path must match +the regex for an ISO Date value.
            • byTimestamp(): The value taken from the producer’s response via the provided JSON Path must +match the regex for an ISO DateTime value.
            • byTime(): The value taken from the producer’s response via the provided JSON Path must match +the regex for an ISO Time value.
            • byType(): The value taken from the producer’s response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. byType can take a closure, in which you can set minOccurrence and maxOccurrence. That way, you can assert the size of the flattened collection. To check the size of an -unflattened collection, use a custom method with the byCommand(…​) testMatcher.
            • byCommand(…​): The value taken from the response via the provided JSON Path is +unflattened collection, use a custom method with the byCommand(…​) testMatcher.

            • byCommand(…​): The value taken from the producer’s response via the provided JSON Path is 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.

          Consider the following example:

          Contract contractDsl = Contract.make {
          +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]:

      - path: $.foo
      +  type: by_regex
      +  predefined: only_alpha_unicode

      Below you can find the allowed list of `type`s.

      • For stubMatchers:

        • by_equality
        • by_regex
        • by_date
        • by_timestamp
        • by_time
      • For testMatchers:

        • by_equality
        • by_regex
        • by_date
        • by_timestamp
        • by_time
        • by_type

          • there are 2 additional fields accepted: minOccurrence and maxOccurrence.
        • by_command
        • by_null

      Consider the following example:

      Groovy DSL.  +

      Contract contractDsl = Contract.make {
       	request {
       		method 'GET'
       		urlPath '/get'
      @@ -3263,7 +4544,7 @@ following, depending on the JSON path:

        'complex.key' : 'foo' ] ]) - stubMatchers { + bodyMatchers { jsonPath('$.duck', byRegex("[0-9]{3}")) jsonPath('$.duck', byEquality()) jsonPath('$.alpha', byRegex(onlyAlphaUnicode())) @@ -3280,11 +4561,15 @@ following, depending on the JSON path:

          200 + 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", @@ -3304,9 +4589,10 @@ following, depending on the JSON path:

            '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 @@ -3315,6 +4601,10 @@ following, depending on the JSON path:

              '$.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()) @@ -3346,17 +4636,152 @@ following, depending on the JSON path:

                // 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}')))) } } -}

      In the preceding example, you can see the dynamic portions of the contract in the +}

      +

      YAML.  +

      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'
      +    nullValue: null
      +  matchers:
      +    headers:
      +      - key: Content-Type
      +        regex: "application/json.*"
      +    body:
      +      - path: $.duck
      +        type: by_regex
      +        value: "[0-9]{3}"
      +      - path: $.duck
      +        type: by_equality
      +      - path: $.alpha
      +        type: by_regex
      +        predefined: only_alpha_unicode
      +      - path: $.alpha
      +        type: by_equality
      +      - path: $.number
      +        type: by_regex
      +        predefined: number
      +      - path: $.aBoolean
      +        type: by_regex
      +        predefined: any_boolean
      +      - path: $.date
      +        type: by_date
      +      - path: $.dateTime
      +        type: by_timestamp
      +      - path: $.time
      +        type: by_time
      +      - path: "$.['key'].['complex.key']"
      +        type: by_equality
      +      - path: $.nullvalue
      +        type: by_null
      +  headers:
      +    Content-Type: application/json
      +response:
      +  status: 200
      +  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"
      +    valueWithMin:
      +      - 1
      +      - 2
      +      - 3
      +    valueWithMax:
      +      - 1
      +      - 2
      +      - 3
      +    valueWithMinMax:
      +      - 1
      +      - 2
      +      - 3
      +    valueWithMinEmpty: []
      +    valueWithMaxEmpty: []
      +    key:
      +      'complex.key' : 'foo'
      +    nulValue: null
      +  matchers:
      +    headers:
      +      - key: Content-Type
      +        regex: "application/json.*"
      +    body:
      +      - path: $.duck
      +        type: by_regex
      +        value: "[0-9]{3}"
      +      - path: $.duck
      +        type: by_equality
      +      - path: $.alpha
      +        type: by_regex
      +        predefined: only_alpha_unicode
      +      - path: $.alpha
      +        type: by_equality
      +      - path: $.number
      +        type: by_regex
      +        predefined: number
      +      - path: $.aBoolean
      +        type: by_regex
      +        predefined: any_boolean
      +      - path: $.date
      +        type: by_date
      +      - path: $.dateTime
      +        type: by_timestamp
      +      - path: $.time
      +        type: by_time
      +      - path: $.valueWithTypeMatch
      +        type: by_type
      +      - path: $.valueWithMin
      +        type: by_type
      +        minOccurrence: 1
      +      - path: $.valueWithMax
      +        type: by_type
      +        maxOccurrence: 3
      +      - path: $.valueWithMinMax
      +        type: by_type
      +        minOccurrence: 1
      +        maxOccurrence: 3
      +      - path: $.valueWithMinEmpty
      +        type: by_type
      +        minOccurrence: 0
      +      - path: $.valueWithMaxEmpty
      +        type: by_type
      +        maxOccurrence: 0
      +      - path: $.duck
      +        type: by_command
      +        value: assertThatValueIsANumber($it)
      +      - path: $.nullValue
      +        type: by_null
      +        value: null
      +  headers:
      +    Content-Type: application/json

      +

      In the preceding example, you can see the dynamic portions of the contract in the matchers sections. For the request part, you can see that, for all fields but 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 @@ -3367,7 +4792,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)
    @@ -3378,83 +4803,84 @@ separates the autogenerated assertions and the assertion from matchers):

    "Content-Type")).matches("application/json.*");
     // and:
      DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
    - assertThatJson(parsedJson).field("valueWithoutAMatcher").isEqualTo("foo");
    + assertThatJson(parsedJson).field("['valueWithoutAMatcher']").isEqualTo("foo");
     // and:
      assertThat(parsedJson.read("$.duck", String.class)).matches("[0-9]{3}");
      assertThat(parsedJson.read("$.duck", Integer.class)).isEqualTo(123);
      assertThat(parsedJson.read("$.alpha", String.class)).matches("[\\p{L}]*");
      assertThat(parsedJson.read("$.alpha", String.class)).isEqualTo("abc");
    - assertThat(parsedJson.read("$.number", String.class)).matches("-?\\d*(\\.\\d+)?");
    + assertThat(parsedJson.read("$.number", String.class)).matches("-?(\\d*\\.\\d+|\\d+)");
      assertThat(parsedJson.read("$.aBoolean", String.class)).matches("(true|false)");
      assertThat(parsedJson.read("$.date", String.class)).matches("(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])");
      assertThat(parsedJson.read("$.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])");
      assertThat(parsedJson.read("$.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
      assertThat((Object) parsedJson.read("$.valueWithTypeMatch")).isInstanceOf(java.lang.String.class);
      assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
    - assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).hasSizeGreaterThanOrEqualTo(1);
    + assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).as("$.valueWithMin").hasSizeGreaterThanOrEqualTo(1);
      assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
    - assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).hasSizeLessThanOrEqualTo(3);
    + assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).as("$.valueWithMax").hasSizeLessThanOrEqualTo(3);
      assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
    - assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).hasSizeBetween(1, 3);
    + assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).as("$.valueWithMinMax").hasSizeBetween(1, 3);
      assertThat((Object) parsedJson.read("$.valueWithMinEmpty")).isInstanceOf(java.util.List.class);
    - assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).hasSizeGreaterThanOrEqualTo(0);
    + assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).as("$.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0);
      assertThat((Object) parsedJson.read("$.valueWithMaxEmpty")).isInstanceOf(java.util.List.class);
    - assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).hasSizeLessThanOrEqualTo(0);
    - assertThatValueIsANumber(parsedJson.read("$.duck"));
    [Important]Important

    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]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 the method name and passed the proper JSON path as a parameter to it.

    The resulting WireMock stub is in the following example:

    				'''
     {
       "request" : {
    -    "urlPath" : "/get",
    -    "method" : "POST",
    -    "headers" : {
    -      "Content-Type" : {
    -        "matches" : "application/json.*"
    -      }
    -    },
    -    "bodyPatterns" : [ {
    -      "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]"
    -    }, {
    -      "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"
    -    }, {
    -      "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"
    -    }, {
    -      "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.duck == 123)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.alpha == 'abc')]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.number =~ /(-?\\\\d*(\\\\.\\\\d+)?)/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.dateTime =~ /(([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]))/)]"
    -    }, {
    -      "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
    -    }, {
    -      "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]"
    -    } ]
    +	"urlPath" : "/get",
    +	"method" : "POST",
    +	"headers" : {
    +	  "Content-Type" : {
    +		"matches" : "application/json.*"
    +	  }
    +	},
    +	"bodyPatterns" : [ {
    +	  "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]"
    +	}, {
    +	  "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"
    +	}, {
    +	  "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"
    +	}, {
    +	  "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.duck == 123)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.alpha == 'abc')]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.dateTime =~ /(([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]))/)]"
    +	}, {
    +	  "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"
    +	}, {
    +	  "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]"
    +	} ]
       },
       "response" : {
    -    "status" : 200,
    -    "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}",
    -    "headers" : {
    -      "Content-Type" : "application/json"
    -    }
    +	"status" : 200,
    +	"body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}",
    +	"headers" : {
    +	  "Content-Type" : "application/json"
    +	}
       }
     }
    -'''
    [Important]Important

    If you use a matcher, then the part of the request aned response that the +'''

    [Important]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 {
    @@ -3463,7 +4889,7 @@ collection.

    Consider the following example:

    "/foo") } response { - status 200 + status OK() body(events: [[ operation : 'EXPORT', eventId : '16f1ed75-0bcc-4f0d-a04d-3121798faf99', @@ -3475,7 +4901,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('.+')) @@ -3520,17 +4946,22 @@ 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:

    org.springframework.cloud.contract.spec.Contract.make {
    +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()
             url '/get'
         }
         response {
    -        status 200
    +        status OK()
             body 'Passed'
             async()
         }
    -}

    8.8 Working with Context Paths

    Spring Cloud Contract supports context paths.

    [Important]Important

    The only change needed to fully support context paths is the switch on the +}

    +

    YAML.  +

    response:
    +    async: true

    +

    8.8 Working with Context Paths

    Spring Cloud Contract supports context paths.

    [Important]Important

    The only change needed to fully support context paths is the switch on the PRODUCER side. Also, the autogenerated tests must use EXPLICIT mode. The consumer side remains untouched. In order for the generated test to pass, you must use EXPLICIT mode.

    Maven.  @@ -3555,7 +4986,7 @@ socket.

    Consider the following contract:

    or
     		url '/my-context-path/url'
     	}
     	response {
    -		status 200
    +		status OK()
     	}
     }

    The following example shows how to set up a base class and Rest Assured:

    import io.restassured.RestAssured;
     import org.junit.Before;
    @@ -3574,9 +5005,36 @@ socket.

    Consider the following contract:

    or
     	}
     }

    If you do it this way:

    • All of your requests in the autogenerated tests are sent to the real endpoint with your context path included (for example, /my-context-path/url).
    • Your contracts reflect that you have a context path. Your generated stubs also have -that information (for example, in the stubs, you have to call /my-context-path/url).

    8.9 Messaging Top-Level Elements

    The DSL for messaging looks a little bit different than the one that focuses on HTTP. The -following sections explain the differences:

    8.9.1 Output Triggered by a Method

    The output message can be triggered by calling a method (such as a Scheduler when a was -started and a message was sent), as shown in the following example:

    def dsl = Contract.make {
    +that information (for example, in the stubs, you have to call /my-context-path/url).

    8.9 Working with Web Flux

    Spring Cloud Contract requires the usage of EXPLICIT mode in your generated tests +to work with Web Flux.

    Maven.  +

    <plugin>
    +    <groupId>org.springframework.cloud</groupId>
    +    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +    <version>${spring-cloud-contract.version}</version>
    +    <extensions>true</extensions>
    +    <configuration>
    +        <testMode>EXPLICIT</testMode>
    +    </configuration>
    +</plugin>

    +

    Gradle.  +

    contracts {
    +		testMode = 'EXPLICIT'
    +}

    +

    The following example shows how to set up a base class and Rest Assured for Web Flux:

    @RunWith(SpringRunner.class)
    +@SpringBootTest(classes = BeerRestBase.Config.class,
    +		webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    +		properties = "server.port=0")
    +public abstract class BeerRestBase {
    +
    +    // your tests go here
    +
    +    // in this config class you define all controllers and mocked services
    +    include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=config,indent=0]
    +
    +}

    8.10 Messaging Top-Level Elements

    The DSL for messaging looks a little bit different than the one that focuses on HTTP. The +following sections explain the differences:

    8.10.1 Output Triggered by a Method

    The output message can be triggered by calling a method (such as a Scheduler when a was +started and a message was sent), as shown in the following example:

    Groovy DSL.  +

    def dsl = Contract.make {
     	// Human readable description
     	description 'Some description'
     	// Label by means of which the output message can be triggered
    @@ -3597,11 +5055,31 @@ started and a message was sent), as shown in the following example:

    'BOOK-NAME', 'foo')
     		}
     	}
    -}

    In the previous example case, the output message is sent to output if a method called +}

    +

    YAML.  +

    # Human readable description
    +description: Some description
    +# Label by means of which the output message can be triggered
    +label: some_label
    +input:
    +  # the contract will be triggered by a method
    +  triggeredBy: bookReturnedTriggered()
    +# output message of the contract
    +outputMessage:
    +  # destination to which the output message will be sent
    +  sentTo: output
    +  # the body of the output message
    +  body:
    +    bookName: foo
    +  # the headers of the output message
    +  headers:
    +    BOOK-NAME: foo

    +

    In the previous example case, the output message is sent to output if a method called bookReturnedTriggered is executed. On the message publisher’s side, we generate a test that calls that method to trigger the message. On the consumer side, you can use -the some_label to trigger the message.

    8.9.2 Output Triggered by a Message

    The output message can be triggered by receiving a message, as shown in the following -example:

    def dsl = Contract.make {
    +the some_label to trigger the message.

    8.10.2 Output Triggered by a Message

    The output message can be triggered by receiving a message, as shown in the following +example:

    Groovy DSL.  +

    def dsl = Contract.make {
     	description 'Some Description'
     	label 'some_label'
     	// input is a message
    @@ -3626,11 +5104,36 @@ example:

    def dsl = Contract.make {
     			header('BOOK-NAME', 'foo')
     		}
     	}
    -}

    In the preceding example, the output message is sent to output if a proper message is +}

    +

    YAML.  +

    # Human readable description
    +description: Some description
    +# Label by means of which the output message can be triggered
    +label: some_label
    +# input is a message
    +input:
    +  messageFrom: input
    +  # has the following body
    +  messageBody:
    +    bookName: 'foo'
    +  # and the following headers
    +  messageHeaders:
    +    sample: 'header'
    +# output message of the contract
    +outputMessage:
    +  # destination to which the output message will be sent
    +  sentTo: output
    +  # the body of the output message
    +  body:
    +    bookName: foo
    +  # the headers of the output message
    +  headers:
    +    BOOK-NAME: foo

    +

    In the preceding example, the output message is sent to output if a proper message is received on the input destination. On the message publisher’s side, the engine generates a test that sends the input message to the defined destination. On the consumer side, you can either send a message to the input destination or use a label -(some_label in the example) to trigger the message.

    8.9.3 Consumer/Producer

    In HTTP, you have a notion of client/stub and `server/test notation. You can also +(some_label in the example) to trigger the message.

    8.10.3 Consumer/Producer

    [Important]Important

    This section is valid only for Groovy DSL.

    In HTTP, you have a notion of client/stub and `server/test notation. You can also use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also provides the consumer and producer methods, as presented in the following example (note that you can use either $ or value methods to provide consumer and producer @@ -3651,11 +5154,12 @@ parts):

    Contract.make {
     				bookName: 'foo'
     		])
     	}
    -}

    8.9.4 Common

    In the input {} or outputMessage {} section you can call assertThat with the name +}

    8.10.4 Common

    In the input or outputMessage section you can call assertThat with the name of a method (e.g. assertThatMessageIsOnTheQueue()) that you have defined in the -base class or in a static import. Spring Cloud Pipelines will execute that method -in the genertaed test.

    8.10 Multiple Contracts in One File

    You can define multiple contracts in one file. Such a contract might resemble the -following example:

    import org.springframework.cloud.contract.spec.Contract
    +base class or in a static import. Spring Cloud Contract will execute that method
    +in the generated test.

    8.11 Multiple Contracts in One File

    You can define multiple contracts in one file. Such a contract might resemble the +following example:

    Groovy DSL.  +

    import org.springframework.cloud.contract.spec.Contract
     
     [
             Contract.make {
    @@ -3665,7 +5169,7 @@ following example:

    '/users/1')
                 }
                 response {
    -                status 200
    +                status OK()
                 }
             },
             Contract.make {
    @@ -3674,10 +5178,26 @@ following example:

    '/users/2')
                 }
                 response {
    -                status 200
    +                status OK()
                 }
             }
    -]

    In the preceding example, one contract has the name field and the other does not. This +]

    +

    YAML.  +

    ---
    +name: should post a user
    +request:
    +  method: POST
    +  url: /users/1
    +response:
    +  status: 200
    +
    +---
    +request:
    +  method: POST
    +  url: /users/2
    +response:
    +  status: 200

    +

    In the preceding example, one contract has the name field and the other does not. This leads to generation of two tests that look more or less like this:

    package org.springframework.cloud.contract.verifier.tests.com.hello;
     
     import com.example.TestBase;
    @@ -3725,8 +5245,96 @@ leads to generation of two tests that look more or less like this:

    The generated stubs is shown in the following example:

    should post a user.json
     1_WithList.json

    As you can see, the first file got the name parameter from the contract. The second got the name of the contract file (WithList.groovy) prefixed with the index (in this -case, the contract had an index of 1 in the list of contracts in the file).

    [Tip]Tip

    As you can see, it iss much better if you name your contracts because doing so makes -your tests far more meaningful.

    9. Customization

    You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in +case, the contract had an index of 1 in the list of contracts in the file).

    [Tip]Tip

    As you can see, it is much better if you name your contracts because doing so makes +your tests far more meaningful.

    8.12 Generating Spring REST Docs snippets from the contracts

    When you want to include the requests and responses of your API using Spring REST Docs, +you only need to make some minor changes to your setup if you are using MockMvc and RestAssuredMockMvc. +Simply include the following dependencies if you haven’t already.

    Maven.  +

    <dependency>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-starter-contract-verifier</artifactId>
    +	<scope>test</scope>
    +</dependency>
    +<dependency>
    +	<groupId>org.springframework.restdocs</groupId>
    +	<artifactId>spring-restdocs-mockmvc</artifactId>
    +	<optional>true</optional>
    +</dependency>

    +

    Gradle.  +

    testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
    +testCompile 'org.springframework.restdocs:spring-restdocs-mockmvc'

    +

    Next you need to make some changes to your base class like the following example.

    package com.example.fraud;
    +
    +import io.restassured.module.mockmvc.RestAssuredMockMvc;
    +
    +import org.junit.Before;
    +import org.junit.Rule;
    +import org.junit.rules.TestName;
    +import org.junit.runner.RunWith;
    +
    +import org.springframework.beans.factory.annotation.Autowired;
    +import org.springframework.boot.test.context.SpringBootTest;
    +import org.springframework.restdocs.JUnitRestDocumentation;
    +import org.springframework.test.context.junit4.SpringRunner;
    +import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    +import org.springframework.web.context.WebApplicationContext;
    +
    +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
    +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
    +
    +@RunWith(SpringRunner.class)
    +@SpringBootTest(classes = Application.class)
    +public abstract class FraudBaseWithWebAppSetup {
    +
    +	private static final String OUTPUT = "target/generated-snippets";
    +
    +	@Rule
    +	public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
    +
    +	@Rule public TestName testName = new TestName();
    +
    +	@Autowired
    +	private WebApplicationContext context;
    +
    +	@Before
    +	public void setup() {
    +	RestAssuredMockMvc.mockMvc(MockMvcBuilders.webAppContextSetup(this.context)
    +			.apply(documentationConfiguration(this.restDocumentation))
    +			.alwaysDo(document(getClass().getSimpleName() + "_" + testName.getMethodName()))
    +			.build());
    +	}
    +
    +	protected void assertThatRejectionReasonIsNull(Object rejectionReason) {
    +		assert rejectionReason == null;
    +	}
    +}

    In case you are using the standalone setup, you can set up RestAssuredMockMvc like this:

    package com.example.fraud;
    +
    +import io.restassured.module.mockmvc.RestAssuredMockMvc;
    +import org.junit.Before;
    +import org.junit.Rule;
    +import org.junit.rules.TestName;
    +import org.springframework.restdocs.JUnitRestDocumentation;
    +import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    +
    +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
    +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
    +
    +public abstract class FraudBaseWithStandaloneSetup {
    +
    +	private static final String OUTPUT = "target/generated-snippets";
    +
    +	@Rule
    +	public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
    +
    +	@Rule public TestName testName = new TestName();
    +
    +	@Before
    +	public void setup() {
    +		RestAssuredMockMvc.standaloneSetup(MockMvcBuilders.standaloneSetup(new FraudDetectionController())
    +				.apply(documentationConfiguration(this.restDocumentation))
    +				.alwaysDo(document(getClass().getSimpleName() + "_" + testName.getMethodName())));
    +	}
    +
    +}
    [Tip]Tip

    You don’t need to specify the output directory for the generated snippets since version 1.2.0.RELEASE of Spring REST Docs.

    9. Customization

    [Important]Important

    This section is valid only for Groovy DSL

    You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in the remainder of this section.

    9.1 Extending the DSL

    You can provide your own functions to the DSL. The key requirement for this feature is to maintain the static compatibility. Later in this document, you can see examples of:

    • Creating a JAR with reusable classes.
    • Referencing of these classes in the DSLs.

    You can find the full example here.

    9.1.1 Common JAR

    The following examples show three classes that can be reused in the DSLs.

    PatternUtils contains functions used by both the consumer and the producer.

    package com.example;
    @@ -3945,19 +5553,7 @@ such as YAML, RAML or PACT. In those cases, you still want to benefit from the a
     generation of tests and stubs. You can add your own implementation for generating both
     tests and stubs. Also, you can customize the way tests are generated (for example, you
     can generate tests for other languages) and the way stubs are generated (for example, you
    -can generate stubs for other HTTP server implementations).

    10.1 Custom Contract Converter

    Assume that your contract is written in a YAML file as follows:

    request:
    -  url: /foo
    -  method: PUT
    -  headers:
    -    foo: bar
    -  body:
    -    foo: bar
    -response:
    -  status: 200
    -  headers:
    -    foo2: bar
    -  body:
    -    foo2: bar

    The ContractConverter interface lets you register your own implementation of a contract +can generate stubs for other HTTP server implementations).

    10.1 Custom Contract Converter

    The ContractConverter interface lets you register your own implementation of a contract structure converter. The following code listing shows the ContractConverter interface:

    package org.springframework.cloud.contract.spec
     
     /**
    @@ -3998,89 +5594,16 @@ structure converter. The following code listing shows the 
     }

    Your implementation must define the condition on which it should start the conversion. Also, you must define how to perform that conversion in both directions.

    [Important]Important

    Once you create your implementation, you must create a /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:

    # Converters
    -org.springframework.cloud.contract.spec.ContractConverter=\
    -org.springframework.cloud.contract.verifier.converter.YamlContractConverter
    -
    -# tag::extension[]
    -org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\
    -org.springframework.cloud.contract.verifier.dsl.wiremock.TestWireMockExtensions
    -# end::extension[]

    The following example shows a typical YAML implementation that matches the preceding -example:

    package org.springframework.cloud.contract.verifier.converter
    -
    -import java.nio.file.Files
    -
    -import groovy.transform.CompileStatic
    -import org.springframework.cloud.contract.spec.Contract
    -import org.springframework.cloud.contract.spec.ContractConverter
    -import org.springframework.cloud.contract.spec.internal.Headers
    -import org.yaml.snakeyaml.Yaml
    -
    -/**
    - * Simple converter from and to a {@link YamlContract} to a collection of {@link Contract}
    - */
    -@CompileStatic
    -class YamlContractConverter implements ContractConverter<List<YamlContract>> {
    -
    -	@Override
    -	public boolean isAccepted(File file) {
    -		String name = file.getName()
    -		return name.endsWith(".yml") || name.endsWith(".yaml")
    -	}
    -
    -	@Override
    -	public Collection<Contract> convertFrom(File file) {
    -		try {
    -			YamlContract yamlContract = new Yaml().loadAs(
    -					Files.newInputStream(file.toPath()), YamlContract.class)
    -			return [Contract.make {
    -				request {
    -					method(yamlContract?.request?.method)
    -					url(yamlContract?.request?.url)
    -					headers {
    -						yamlContract?.request?.headers?.each { String key, Object value ->
    -							header(key, value)
    -						}
    -					}
    -					body(yamlContract?.request?.body)
    -				}
    -				response {
    -					status(yamlContract?.response?.status)
    -					headers {
    -						yamlContract?.response?.headers?.each { String key, Object value ->
    -							header(key, value)
    -						}
    -					}
    -					body(yamlContract?.response?.body)
    -				}
    -			}]
    -		}
    -		catch (FileNotFoundException e) {
    -			throw new IllegalStateException(e)
    -		}
    -	}
    -
    -	@Override
    -	public List<YamlContract> convertTo(Collection<Contract> contracts) {
    -		return contracts.collect { Contract contract ->
    -			YamlContract yamlContract = new YamlContract()
    -			yamlContract.request.with {
    -				method = contract?.request?.method?.clientValue
    -				url = contract?.request?.url?.clientValue
    -				headers = (contract?.request?.headers as Headers)?.asStubSideMap()
    -				body = contract?.request?.body?.clientValue as Map
    -			}
    -			yamlContract.response.with {
    -				status = contract?.response?.status?.clientValue as Integer
    -				headers = (contract?.response?.headers as Headers)?.asStubSideMap()
    -				body = contract?.response?.body?.clientValue as Map
    -			}
    -			return yamlContract
    -		}
    -	}
    -}

    10.1.1 Pact Converter

    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 +implementation.

    The following example shows a typical spring.factories file:

    org.springframework.cloud.contract.spec.ContractConverter=\
    +org.springframework.cloud.contract.verifier.converter.YamlContractConverter

    10.1.1 Pact Converter

    Spring Cloud Contract includes support for Pact representation of +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"
    @@ -4101,10 +5624,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" + } } } }, @@ -4118,9 +5667,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" + } } } } @@ -4128,13 +5695,13 @@ 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" } } -}

    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>
    @@ -4148,19 +5715,13 @@ the current Pact version that you use.

    Maven.  <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-contract-spec-pact</artifactId> + <artifactId>spring-cloud-contract-pact</artifactId> <version>${spring-cloud-contract.version}</version> </dependency> - <dependency> - <groupId>au.com.dius</groupId> - <artifactId>pact-jvm-model</artifactId> - <version>2.4.18</version> - </dependency> </dependencies> </plugin>

    Gradle.  -

    classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
    -classpath 'au.com.dius:pact-jvm-model:2.4.18'

    +

    classpath "org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}"

    When you execute the build of your application, a test will be generated. The generated test might be as follows:

    @Test
     public void validate_shouldMarkClientAsFraud() throws Exception {
    @@ -4175,24 +5736,25 @@ test might be as follows:

    // 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})/)]"
         } ]
    @@ -4202,25 +5764,19 @@ test might be as follows:

    "body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
         "headers" : {
           "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
    -    }
    -  }
    +    },
    +    "transformers" : [ "response-template" ]
    +  },
     }

    10.1.4 Pact for Consumers

    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. 

    <dependency>
     	<groupId>org.springframework.cloud</groupId>
    -	<artifactId>spring-cloud-contract-spec-pact</artifactId>
    -	<scope>test</scope>
    -</dependency>
    -<dependency>
    -	<groupId>au.com.dius</groupId>
    -	<artifactId>pact-jvm-model</artifactId>
    -	<version>2.4.18</version>
    +	<artifactId>spring-cloud-contract-pact</artifactId>
     	<scope>test</scope>
     </dependency>

    Gradle.  -

    testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
    -testCompile 'au.com.dius:pact-jvm-model:2.4.18'

    +

    testCompile "org.springframework.cloud:spring-cloud-contract-pact"

    10.2 Using the Custom Test Generator

    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
    @@ -4404,16 +5960,53 @@ implementation is used. If you provide more than one, the first one on the list
     }

    Then you can register it in your spring.factories file, as shown in the following example:

    # Example of a custom Stub Downloader Provider
     org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
    -com.example.CustomStubDownloaderBuilder

    Now you can pick a folder with the source of your stubs.

    [Important]Important

    If you do not provide any implementation, then the default is used. -If you use the repositoryRoot property or the workOffline flag, then an Aether-based -implementation that downloads stubs from a remote repository is used. If you do not -provide these values, the ClasspathStubProvider (which will scan the classpath) is -used. If you provide more than one, then the first one on the list is used.

    11. Spring Cloud Contract WireMock

    The Spring Cloud Contract WireMock modules let you use WireMock in a +com.example.CustomStubDownloaderBuilder

    Now you can pick a folder with the source of your stubs.

    [Important]Important

    If you do not provide any implementation, then the default is used (scan classpath). +If you provide the stubsMode = StubRunnerProperties.StubsMode.LOCAL or +, stubsMode = StubRunnerProperties.StubsMode.REMOTE then the Aether implementation will be used +If you provide more than one, then the first one on the list is used.

    10.6 Using the SCM Stub Downloader

    Whenever the repositoryRoot starts with a SCM protocol +(currently we support only git://), the stub downloader will try +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

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop) +

    * stubrunner.properties.git.branch (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop) +

    * stubrunner.properties.git.username (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop) +

    * stubrunner.properties.git.password (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop) +

    * stubrunner.properties.git.no-of-attempts (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop) +

    * stubrunner.properties.git.wait-between-attempts (system prop) +

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol +(starts with pact://), the stub downloader will try +to fetch the Pact contract definitions from the Pact Broker. +Whatever is set after pact:// will be parsed as the Pact Broker URL.

    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

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop) +

    * stubrunner.properties.pactbroker.host (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop) +

    * stubrunner.properties.pactbroker.port (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop) +

    * stubrunner.properties.pactbroker.protocol (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop) +

    * stubrunner.properties.pactbroker.tags (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop) +

    * stubrunner.properties.pactbroker.auth.scheme (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop) +

    * stubrunner.properties.pactbroker.auth.username (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop) +

    * stubrunner.properties.pactbroker.auth.password (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop) +

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop) +

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    11. Spring Cloud Contract WireMock

    The Spring Cloud Contract WireMock modules let you use WireMock in a Spring Boot application. Check out the -samples +samples for more details.

    If you have a Spring Boot application that uses Tomcat as an embedded server (which is the default with spring-boot-starter-web), you can add -spring-cloud-contract-wiremock to your classpath and add @AutoConfigureWireMock in +spring-cloud-starter-contract-stub-runner to your classpath and add @AutoConfigureWireMock in order to be able to use Wiremock in your tests. Wiremock runs as a stub server and you can register stub behavior using a Java API or via static JSON declarations as part of your test. The following code shows an example:

    @RunWith(SpringRunner.class)
    @@ -4520,7 +6113,8 @@ annotation or the stub runner. If you use the JUnit @Rule<
     classpath and it is selected by the RestTemplateBuilder and configured to ignore SSL
     errors. If you use the default java.net client, you do not need the annotation (but it
     won’t do any harm). There is no support currently for other clients, but it may be added
    -in future releases.

    11.5 WireMock and Spring MVC Mocks

    Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into +in future releases.

    To disable the custom RestTemplateBuilder, set the wiremock.rest-template-ssl-enabled +property to false.

    11.5 WireMock and Spring MVC Mocks

    Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a Spring MockRestServiceServer. The following code shows an example:

    @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment = WebEnvironment.NONE)
     public class WiremockForDocsMockServerApplicationTests {
    @@ -4551,13 +6145,22 @@ pattern. The JSON format is the normal WireMock format, which you can read about
     WireMock website.

    Currently, the Spring Cloud Contract Verifier supports Tomcat, Jetty, and Undertow as Spring Boot embedded servers, and Wiremock itself has "native" support for a particular version of Jetty (currently 9.2). To use the native Jetty, you need to add the native -Wiremock dependencies and exclude the Spring Boot container (if there is one).

    11.6 Generating Stubs using REST Docs

    Spring REST Docs can be used to generate -documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc or +Wiremock dependencies and exclude the Spring Boot container (if there is one).

    11.6 Customization of WireMock configuration

    You can register a bean of org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer type +in order to customize the WireMock configuration (e.g. add custom transformers). +Example:

    		@Bean WireMockConfigurationCustomizer optionsCustomizer() {
    +			return new WireMockConfigurationCustomizer() {
    +				@Override public void customize(WireMockConfiguration options) {
    +// perform your customization here
    +				}
    +			};
    +		}

    11.7 Generating Stubs using REST Docs

    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 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 -example:

    @RunWith(SpringRunner.class)
    +example using MockMvc:

    @RunWith(SpringRunner.class)
     @SpringBootTest
     @AutoConfigureRestDocs(outputDir = "target/snippets")
     @AutoConfigureMockMvc
    @@ -4573,32 +6176,49 @@ example:

    "resource"));
     	}
     }

    This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches -all GET requests to the "/resource" path.

    Without any additional configuration, this tests creates a stub with a request matcher +all GET requests to the "/resource" path. The same example with WebTestClient (used +for testing Spring WebFlux applications) would look like this:

    @RunWith(SpringRunner.class)
    +@SpringBootTest
    +@AutoConfigureRestDocs(outputDir = "target/snippets")
    +@AutoConfigureWebTestClient
    +public class ApplicationTests {
    +
    +	@Autowired
    +	private WebTestClient client;
    +
    +	@Test
    +	public void contextLoads() throws Exception {
    +		client.get().uri("/resource").exchange()
    +				.expectBody(String.class).isEqualTo("Hello World")
    + 				.consumeWith(document("resource"));
    +	}
    +}

    Without any additional configuration, these tests create a stub with a request matcher for the HTTP method and all headers except "host" and "content-length". To match the request more precisely (for example, to match the body of a POST or PUT), we need to explicitly create a request matcher. Doing so has two effects:

    • Creating a stub that matches only in the way you specify.
    • Asserting that the request in the test case also matches the same conditions.

    The main entry point for this feature is WireMockRestDocs.verify(), which can be used as a substitute for the document() convenience method, as shown in the following -example:

    @RunWith(SpringRunner.class)
    -@SpringBootTest
    -@AutoConfigureRestDocs(outputDir = "target/snippets")
    -@AutoConfigureMockMvc
    -public class ApplicationTests {
    +example:

    import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
    @RunWith(SpringRunner.class)
    +@SpringBootTest
    +@AutoConfigureRestDocs(outputDir = "target/snippets")
    +@AutoConfigureMockMvc
    +public class ApplicationTests {
     
    -	@Autowired
    -	private MockMvc mockMvc;
    +	@Autowired
    +	private MockMvc mockMvc;
     
    -	@Test
    -	public void contextLoads() throws Exception {
    -		mockMvc.perform(post("/resource")
    -                .content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
    +	@Test
    +	public void contextLoads() throws Exception {
    +		mockMvc.perform(post("/resource")
    +                .content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
     				.andExpect(status().isOk())
    -				.andDo(verify().jsonPath("$.id")
    -                        .stub("resource"));
    +				.andDo(verify().jsonPath("$.id")
    +                        .stub("resource"));
     	}
     }

    This contract specifies that any valid POST with an "id" field receives the response defined in this test. You can chain together calls to .jsonPath() to add additional matchers. If JSON Path is unfamiliar, The JayWay -documentation can help you get up to speed.

    Instead of the jsonPath and contentType convenience methods, you can also use the +documentation can help you get up to speed. The WebTestClient version of this test +has a similar verify() static helper that you insert in the same place.

    Instead of the jsonPath and contentType convenience methods, you can also use the WireMock APIs to verify that the request matches the created stub, as shown in the following example:

    @Test
     public void contextLoads() throws Exception {
    @@ -4633,11 +6253,10 @@ range of parameters. The above example generates a stub resembling the following
     

    [Note]Note

    You can use either the wiremock() method or the jsonPath() and contentType() methods to create request matchers, but you can’t use both approaches.

    On the consumer side, you can make the resource.json generated earlier in this section available on the classpath (by -publishing -stubs as JARs, for example). After that, you can create a stub using WireMock in a +<<publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a number of different ways, including by using @AutoConfigureWireMock(stubs="classpath:resource.json"), as described earlier in this -document.

    11.7 Generating Contracts by Using REST Docs

    You can also generate Spring Cloud Contract DSL files and documentation with Spring REST +document.

    11.8 Generating Contracts by Using REST Docs

    You can also generate Spring Cloud Contract DSL files and documentation with Spring REST Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts and the stubs.

    Why would you want to use this feature? Some people in the community asked questions about a situation in which they would like to move to DSL-based contract definition, @@ -4648,12 +6267,13 @@ is there because it makes sense to generate both the contracts and the stubs.

    "{\"foo\": 23 }")) + .content("{\"foo\": 23, \"bar\" : \"baz\" }")) .andExpect(status().isOk()) .andExpect(content().string("bar")) // first WireMock .andDo(WireMockRestDocs.verify() .jsonPath("$[?(@.foo >= 20)]") + .jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]") .contentType(MediaType.valueOf("application/json")) .stub("shouldGrantABeerIfOldEnough")) // then Contract DSL documentation @@ -4673,7 +6293,7 @@ Contract.make { } } response { - status 200 + status OK() body(''' bar ''') @@ -4686,7 +6306,8 @@ Contract.make { } } }

    The generated document (formatted in Asciidoc in this case) contains a formatted -contract. The location of this file would be index/dsl-contract.adoc.

    12. Migrations

    This section covers migrating from one version of Spring Cloud Contract Verifier to the +contract. The location of this file would be index/dsl-contract.adoc.

    12. Migrations

    [Tip]Tip

    For up to date migration guides please visit +the project’s wiki page.

    This section covers migrating from one version of Spring Cloud Contract Verifier to the next version. It covers the following versions upgrade paths:

    12.1 1.0.x → 1.1.x

    This section covers upgrading from version 1.0 to version 1.1.

    12.1.1 New structure of generated stubs

    In 1.1.x we have introduced a change to the structure of generated stubs. If you have been using the @AutoConfigureWireMock notation to use the stubs from the classpath, it no longer works. The following example shows how the @AutoConfigureWireMock notation @@ -4779,12 +6400,7 @@ you might see the following exception:

    Failed to
     [ERROR] /some/path/SomeClass.java:[4,39] package com.jayway.restassured.response does not exist

    This exception will occur due to the fact that the tests got generated with an old version of plugin and at test execution time you have an incompatible version of the release train (and vice versa).

    Done via issue 267

    12.3 1.2.x → 2.0.x

    12.3.1 No Camel support

    We will add back Apache Camel support only after this issue -gets fixed

    13. Links

    The following links may be helpful when working with Spring Cloud Contract Verifier:

    \ No newline at end of file diff --git a/2.0.x/spring-cloud-contract-maven-plugin/checkstyle.html b/2.0.x/spring-cloud-contract-maven-plugin/checkstyle.html index a1d3ca99b1..0afe8b8ce1 100644 --- a/2.0.x/spring-cloud-contract-maven-plugin/checkstyle.html +++ b/2.0.x/spring-cloud-contract-maven-plugin/checkstyle.html @@ -1,13 +1,13 @@ - + Spring Cloud Contract Maven Plugin – Checkstyle Results @@ -146,10 +146,10 @@ @@ -313,7 +313,7 @@