From ed8945e933c177868a68e9bde5406e49ad0d02f9 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 19 Oct 2017 12:48:31 +0000 Subject: [PATCH] Sync docs from 1.1.x to gh-pages --- 1.1.x/multi/multi__contract_dsl.html | 385 +++++--- 1.1.x/multi/multi__customization.html | 22 +- ...multi__spring_cloud_contract_wiremock.html | 4 +- ...lti__using_the_pluggable_architecture.html | 461 +++++++++ 1.1.x/multi/multi_spring-cloud-contract.html | 2 +- 1.1.x/single/spring-cloud-contract.html | 507 ++++++---- 1.1.x/spring-cloud-contract.xml | 896 +++++++++++------- 7 files changed, 1543 insertions(+), 734 deletions(-) create mode 100644 1.1.x/multi/multi__using_the_pluggable_architecture.html diff --git a/1.1.x/multi/multi__contract_dsl.html b/1.1.x/multi/multi__contract_dsl.html index 31d9ae4c60..61d3519840 100644 --- a/1.1.x/multi/multi__contract_dsl.html +++ b/1.1.x/multi/multi__contract_dsl.html @@ -1,11 +1,15 @@ - 7. Contract DSL

7. Contract DSL

[Important]Important

Remember that inside the contract file you have to provide the fully qualified name to -the Contract class and the make static import i.e. 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 don’t be alarmed if you didn’t use Groovy before. Knowledge of the language is not really needed as our DSL uses only -a tiny subset of it (namely literals, method calls and closures). What’s more the DSL is designed to be programmer-readable without any knowledge of the DSL itself - - it’s statically typed.

[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.

Let’s look at full example of a contract definition.

org.springframework.cloud.contract.spec.Contract.make {
+   7. Contract DSL

7. Contract DSL

[Important]Important

Remember that, inside the 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 {
 	request {
 		method 'PUT'
 		url '/api/12'
@@ -46,11 +50,16 @@ a tiny subset of it (namely literals, method calls and closures). What’s m
 	response {
 		status 200
 	}
-}

Not all features of the DSL are used in example above. If you didn’t find what you are looking for, please check next paragraphs on this page.

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

7.1 Limitations

[Warning]Warning

Spring Cloud Contract Verifier doesn’t support XML properly. Please use JSON or help us implement this feature.

[Warning]Warning

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

[Warning]Warning

Due to the fact that JSON structure can have any form it’s sometimes impossible to parse it properly when using -the value(consumer(…​), producer(…​)) notation when using that in GString. That’s why we highly recommend using the -Groovy Map notation.

7.2 Common Top-Level elements

7.2.1 Description

You can add a description to your contract that is nothing else but an arbitrary text. Example:

		org.springframework.cloud.contract.spec.Contract.make {
+}
[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

7.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 +is why you should use the Groovy Map notation.

7.2 Common Top-Level elements

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

7.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 {
 			description('''
 given:
 	An input
@@ -59,14 +68,27 @@ when:
 then:
 	Output
 ''')
-		}

7.2.2 Name

You can provide a name of your contract. Let’s assume that you’ve provided a name should register a user. -If you do this then the name of the autogenerated test will be equal to validate_should_register_a_user. -Also the name of the stub will be should_register_a_user.json in case of a WireMock stub.

[Important]Important

Please ensure that the name doesn’t contain any characters that will make the generated test - not possible to compile. Also remember that if you provide the same name for multiple contracts then your - autogenerated tests will fail to compile and your generated stubs will override each other.

7.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 just set the ignored property on the contract itself:

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

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

7.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 {
 	ignored()
-}

7.3 HTTP Top-Level Elements

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 {
+}

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

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/readFromFile.groovy[indent=0]

Further assume that the JSON files is as follows:

request.json

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/request.json[indent=0]

response.json

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/response.json[indent=0]

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.

7.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 {
 	// Definition of HTTP request part of the contract
 	// (this can be a valid request or invalid depending
 	// on type of contract being specified).
@@ -85,7 +107,8 @@ or just set the ignored property on the contract it
 	// Contract priority, which can be used for overriding
 	// contracts (1 is highest). Priority is optional.
 	priority 1
-}

7.4 Request

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 {
+}

7.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 {
 	request {
 		// HTTP request method (GET/POST/PUT/DELETE).
 		method 'GET'
@@ -97,7 +120,8 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

It is possible to specify whole url instead of just path, but urlPath is the recommended way as it makes the tests host-independent.

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

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 {
 	request {
 		method 'GET'
 
@@ -108,7 +132,8 @@ or just set the ignored property on the contract it
 	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 {
+}

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 {
 	request {
 		//...
 
@@ -148,7 +173,7 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

It may contain additional request headers…​

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

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

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -165,7 +190,7 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

…​and a request body.

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

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

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -177,7 +202,8 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

Request may contain multipart elements. Just call the multipart() method.

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

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 {
 	request {
 		method "PUT"
 		url "/multipart"
@@ -200,12 +226,11 @@ or just set the ignored property on the contract it
 	response {
 		status 200
 	}
-}

In this example we defined parameters either directly by using the map notation, -where the value can be a dynamic property (e.g. formParameter: $(consumer(…​), producer(…​))) - or by using the named(…​) method that allows you to set a named parameter. - A named parameter can set a name and content. You can call it either via - a method with 2 arguments: e.g. named("fileName", "fileContent") or - via a map notation named(name: "fileName", content: "fileContent").

From this contract the generated test will look more or less like this:

// given:
+}

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 +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:
  MockMvcRequestSpecification request = given()
    .header("Content-Type", "multipart/form-data;boundary=AaB03x")
    .param("formParameter", "\"formParameterValue\"")
@@ -217,7 +242,7 @@ where the value can be a dynamic property (e.g. formParame
    .put("/multipart");
 
 // then:
- assertThat(response.statusCode()).isEqualTo(200);

The WireMock stub will look more or less like this:

			'''
+ assertThat(response.statusCode()).isEqualTo(200);

The WireMock stub is as follows:

			'''
 {
   "request" : {
 	"url" : "/multipart",
@@ -240,7 +265,8 @@ where the value can be a dynamic property (e.g. formParame
 	"transformers" : [ "response-template" ]
   }
 }
-	'''

7.5 Response

Minimal response must contain HTTP status code.

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

7.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 {
 	request {
 		//...
 	}
@@ -249,19 +275,24 @@ where the value can be a dynamic property (e.g. formParame
 		// in response to request specified above.
 		status 200
 	}
-}

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

7.6 Dynamic properties

The contract can contain some dynamic properties - timestamps / ids etc. You don’t want to enforce the consumers to stub their -clocks to always return the same value of time so that it gets matched by the stub. That’s why we allow you to provide the dynamic -parts in your contracts in two ways. One is to pass them directly in the -body and one to set them in a separate section called testMatchers and stubMatchers.

7.6.1 Dynamic properties inside the body

You can set the properties inside the body either via the value method

value(consumer(...), producer(...))
+}

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).

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

7.5.1 Dynamic properties inside the body

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(...))
 value(stub(...), test(...))
-value(client(...), server(...))

or if you’re using the Groovy map notation for body you can use the $() method

$(consumer(...), producer(...))
+value(client(...), server(...))

The following example shows how to set dynamic properties with $():

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

All of the aforementioned approaches are equal. That means that stub and client methods are aliases over the consumer -method. Let’s take a closer look at what we can do with those values in the subsequent sections.

7.6.2 Regular expressions

You can use regular expressions to write your requests in Contract DSL. It 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 it when you need to use patterns and not exact values both -for your test and your server side tests.

Please see the example below:

org.springframework.cloud.contract.spec.Contract.make {
+$(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.

7.5.2 Regular expressions

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 {
 	request {
 		method('GET')
 		url $(consumer(~/\/[0-9]{2}/), producer('/12'))
@@ -284,8 +315,9 @@ for your test and your server side tests.

Please see the example below:

'Content-Type': 'text/plain' } } -}

You can also provide only one side of the communication using a regular expression. If you do that then automatically we’ll -provide the generated string that matches the provided regular expression. For example:

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

You can also provide only one side of the communication with a regular expression. If you +do so, then the contract engine automatically provides the generated string that matches +the provided regular expression. The following code shows an example:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'PUT'
 		url value(consumer(regex('/foo/[0-9]{5}')))
@@ -305,7 +337,9 @@ provide the generated string that matches the provided regular expression. For e
 			contentType("application/vnd.fraud.v1+json")
 		}
 	}
-}

In this example for request and response the opposite side of the communication will have the respective data generated.

Spring Cloud Contract comes with a series of predefined regular expressions that you can use in your contracts.

protected static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
+}

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 ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
 protected static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?')
 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])')
@@ -378,7 +412,7 @@ String nonEmpty() {
 
 String nonBlank() {
 	return NON_BLANK.pattern()
-}

so in your contract you can use it like this

Contract dslWithOptionalsInString = Contract.make {
+}

In your contract, you can use it as shown in the following example:

Contract dslWithOptionalsInString = Contract.make {
 	priority 1
 	request {
 		method POST()
@@ -401,7 +435,8 @@ String nonBlank() {
 				message: "User not found by email = [${value(producer(regex(email())), consumer('not.existing@user.com'))}]"
 		)
 	}
-}

7.6.3 Passing optional parameters

It is possible to provide optional parameters in your contract. It’s only possible to have optional parameter for the:

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

Example:

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

7.5.3 Passing Optional Parameters

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 {
 		method 'POST'
@@ -423,7 +458,8 @@ String nonBlank() {
 				code: value(consumer("123123"), producer(optional("123123")))
 		)
 	}
-}

By wrapping a part of the body with the optional() method you are in fact creating a regular expression that should be present 0 or more times.

That way for the example above the following test would be generated if you pick Spock:

"""
+}

By wrapping a part of the body with the optional() method, you create a regular +expression that must be present 0 or more times.

If you use Spock for, the following test would be generated from the previous example:

"""
  given:
   def request = given()
     .header("Content-Type", "application/json")
@@ -439,7 +475,7 @@ String nonBlank() {
  and:
   DocumentContext parsedJson = JsonPath.parse(response.body.asString())
   assertThatJson(parsedJson).field("['code']").matches("(123123)?")
-"""

and the following stub:

'''
+"""

The following stub would also be generated:

'''
 {
   "request" : {
     "url" : "/users/password",
@@ -464,8 +500,9 @@ String nonBlank() {
   },
   "priority" : 1
 }
-'''

7.6.4 Executing custom methods on server side

It is also possible to define a method call to be executed on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" -in the configuration. Example:

Contract

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

7.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 +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 {
 		method 'PUT'
 		url $(consumer(regex('^/api/[0-9]{2}$')), producer('/api/12'))
@@ -485,7 +522,7 @@ in the configuration. Example:

Contract200 } -}

Base class

abstract class BaseMockMvcSpec extends Specification {
+}

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

abstract class BaseMockMvcSpec extends Specification {
 
 	def setup() {
 		RestAssuredMockMvc.standaloneSetup(new PairIdController())
@@ -499,13 +536,13 @@ in the configuration. Example:

Contract

[Important]Important

You can’t use both a String and execute to perform concatenation. E.g. calling -header('Authorization', 'Bearer ' + execute('authToken()')) will lead to improper results. -To make this work just call header('Authorization', execute('authToken()')) and ensure that -the authToken() method returns everything that you need.

The type of the object read from the JSON can be one of the followings depending on the -JSON path:

  • String if you point to a String value in a JSON
  • JSONArray if you point to a List in a JSON
  • Map if you point to a Map in a JSON
  • proper Number if you point to Integer, Double etc. in a JSON
  • Boolean if you point to a Boolean in a JSON

In the request part of the contract you can specify that the body should be -taken from a method.

[Important]Important

You have to provide both the consumer and the producer side -and the execute part can be applied for the whole body. Not for parts of it!

Example:

Contract contractDsl = Contract.make {
+}
[Important]Important

You cannot use both a String and execute to perform concatenation. For +example, calling header('Authorization', 'Bearer ' + execute('authToken()')) leads to +improper results. Instead, call header('Authorization', execute('authToken()')) and +ensure that the authToken() method returns everything you need.

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 in the JSON.
  • JSONArray: If you point to a List in the JSON.
  • Map: If you point to a Map in the JSON.
  • Number: If you point to Integer, Double etc. in the JSON.
  • Boolean: If you point to a Boolean in the JSON.

In the request part of the contract, you can specify that the body should be taken from +a method.

[Important]Important

You must provide both the consumer and the producer side. The execute part +is applied for the whole body - not for parts of it.

The following example shows how to read an object from JSON:

Contract contractDsl = Contract.make {
 	request {
 		method 'GET'
 		url '/something'
@@ -516,8 +553,8 @@ and the execute part can be applied for the whole b
 	response {
 		status 200
 	}
-}

This will result in calling the hashCode() method in the request body. -It would more or less like this:

// given:
+}

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

// given:
  MockMvcRequestSpecification request = given()
    .body(hashCode());
 
@@ -526,9 +563,12 @@ It would more or less like this:

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

7.6.5 Referencing request from response

The best situation is to provide fixed values but sometimes you need to reference a request in your response. -In order to do this you can profit from the fromRequest() method that allows you to reference a bunch -of elements from the HTTP request. You can use the following options:

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

Let’s take a look at the following contract

Contract contractDsl = Contract.make {
+ assertThat(response.statusCode()).isEqualTo(200);

7.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 +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 {
 	request {
 		method 'GET'
 		url('/api/v1/xxxx') {
@@ -560,7 +600,7 @@ of elements from the HTTP request. You can use the following options:

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

Running a JUnit test generation will lead in creation of a test looking more or less like this

// given:
+}

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

// given:
  MockMvcRequestSpecification request = given()
    .header("Authorization", "secret")
    .header("Authorization", "secret2")
@@ -577,15 +617,17 @@ of elements from the HTTP request. You can use the following options:

"Authorization")).isEqualTo("foo secret bar"); // and: DocumentContext parsedJson = JsonPath.parse(response.getBody().asString()); - assertThatJson(parsedJson).field("url").isEqualTo("/api/v1/xxxx"); - assertThatJson(parsedJson).field("fullBody").isEqualTo("{\"foo\":\"bar\",\"baz\":5}"); - assertThatJson(parsedJson).field("paramIndex").isEqualTo("bar2"); - assertThatJson(parsedJson).field("responseFoo").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization2").isEqualTo("secret2"); - assertThatJson(parsedJson).field("responseBaz").isEqualTo(5); - assertThatJson(parsedJson).field("responseBaz2").isEqualTo("Bla bla bar bla bla"); - assertThatJson(parsedJson).field("param").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization").isEqualTo("secret");

As you can see elements from the request have been properly referenced in the response.

The generated WireMock stub will look more or less like this:

{
+ assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\"foo\":\"bar\",\"baz\":5}");
+ assertThatJson(parsedJson).field("['authorization']").isEqualTo("secret");
+ assertThatJson(parsedJson).field("['authorization2']").isEqualTo("secret2");
+ assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx");
+ assertThatJson(parsedJson).field("['param']").isEqualTo("bar");
+ assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2");
+ assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1");
+ assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5);
+ assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar");
+ assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2");
+ assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla");

As you can see, elements from the request have been properly referenced in the response.

The generated WireMock stub should resemble the following example:

{
   "request" : {
     "urlPath" : "/api/v1/xxxx",
     "method" : "POST",
@@ -600,22 +642,24 @@ of elements from the HTTP request. You can use the following options:

} }, "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.baz == 5)]" + "matchesJsonPath" : "$[?(@.['baz'] == 5)]" }, { - "matchesJsonPath" : "$[?(@.foo == 'bar')]" + "matchesJsonPath" : "$[?(@.['foo'] == 'bar')]" } ] }, "response" : { "status" : 200, - "body" : "{\"url\":\"{{{request.url}}}\",\"param\":\"{{{request.query.foo.[0]}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\",\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\"}", + "body" : "{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}", "headers" : { - "Authorization" : "{{{request.headers.Authorization.[0]}}}" + "Authorization" : "{{{request.headers.Authorization.[0]}}};foo" }, "transformers" : [ "response-template" ] } -}

So sending a request as the one presented in the request part of the contract will lead in sending the following -response body

{
+}

Sending a request such as the one presented in the request part of the contract results +in sending the following response body:

{
   "url" : "/api/v1/xxxx?foo=bar&foo=bar2",
+  "path" : "/api/v1/xxxx",
+  "pathIndex" : "v1",
   "param" : "bar",
   "paramIndex" : "bar2",
   "authorization" : "secret",
@@ -624,34 +668,41 @@ response body

"responseFoo" : "bar",
   "responseBaz" : 5,
   "responseBaz2" : "Bla bla bar bla bla"
-}
[Important]Important

This feature will work only with WireMock having version greater or equal to 2.5.1. We’re using WireMock’s -response-template response transformer. It’s using Handlebars to convert the Mustache {{{ }}} templates into -proper values. Additionally we’re registering 2 helper functions. escapejsonbody - that escapes the request -body in a format that can be embedded in a JSON. Another is jsonpath that for a given parameter knows how to -find an object in the request body.

7.6.6 Dynamic properties in matchers sections

If you’ve been working with Pact this might seem familiar. Quite a few users -are used to having a separation between the body and setting dynamic parts of your contract.

That’s why you can profit from two separate sections. One is called stubMatchers where you can -define the dynamic values that should end up in a stub. You can set it in the request or inputMessage -part of your contract. The other is called testMatchers which is present in the response or -outputMessage side of the contract.

Currently we support only JSON Path based matchers with the following matching possibilities. -For stubMatchers:

  • byEquality() - the value taken from the response via the provided JSON Path needs -to be equal to the provided value in the contract
  • byRegex(…​) - the value taken from the response via the provided JSON Path needs -to match the regex
  • byDate() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Date
  • byTimestamp() - the value taken from the response via the provided JSON Path needs -to match the regex for ISO DateTime
  • byTime() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Time

For testMatchers:

  • byEquality() - the value taken from the response via the provided JSON Path needs -to be equal to the provided value in the contract
  • byRegex(…​) - the value taken from the response via the provided JSON Path needs -to match the regex
  • byDate() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Date
  • byTimestamp() - the value taken from the response via the provided JSON Path needs -to match the regex for ISO DateTime
  • byTime() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Time
  • byType() - the value taken from the 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 where you can set minOccurrence and maxOccurrence. -That way you can assert on the size of the flattened collection. To check the size -of an unflattened collection, use a custom method via byCommand(…​) testMatcher.
  • byCommand(…​) - the value taken from the response via the provided JSON Path will be -passed as an input to the custom method that you’re providing. E.g. byCommand('foo($it)') -will result in calling a foo method to which the value matching the JSON Path will get - passed.

    • The type of the object read from the JSON can be one of the followings depending on the -JSON path:

      • String if you point to a String value in a JSON
      • JSONArray if you point to a List in a JSON
      • Map if you point to a Map in a JSON
      • proper Number if you point to Integer, Double etc. in a JSON
      • Boolean if you point to a Boolean in a JSON

Let’s take a look at the following example:

Contract contractDsl = Contract.make {
+}
[Important]Important

This feature works only with WireMock having a version greater than or equal +to 2.5.1. The Spring Cloud Contract Verifier uses WireMock’s +response-template response transformer. It uses Handlebars to convert the Mustache {{{ }}} templates into +proper values. Additionally, it registers two helper functions:

  • escapejsonbody: Escapes the request body in a format that can be embedded in a JSON.
  • jsonpath: For a given parameter, find an object in the request body.

7.5.6 Registering Your Own WireMock Extension

WireMock lets you register custom extensions. By default, Spring Cloud Contract registers +the transformer, which lets you reference a request from a response. If you want to +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:

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories[indent=0]

The following is an example of a custom extension:

TestWireMockExtensions.groovy.  +

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/TestWireMockExtensions.groovy[indent=0]

+

[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.

7.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 +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 +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 +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 {
 	request {
 		method 'GET'
 		urlPath '/get'
@@ -757,15 +808,20 @@ JSON path:

    valueWithoutAMatcher we’re setting - explicitly the values of regular expressions we’d like the stub to contain. For the valueWithoutAMatcher - the verification will take place in the same way as without the usage of matchers - the test - will perform an equality check in this case.

    For the response side in the testMatchers section we’re defining all the dynamic parts - in a similar manner. The only difference is that we have the byType matchers too. In that - case we’re checking 4 fields in the way that we’re verifying whether the response from the test - has a value whose JSON path matching the given field is of the same type as the one defined in the response body and:

    • for $.valueWithTypeMatch - we’re just checking the whether the type is the same
    • for $.valueWithMin - we’re checking the type and assert if the size is greater or equal to the min occurrence
    • for $.valueWithMax - we’re checking the type and assert if the size is smaller or equal to the max occurrence
    • for $.valueWithMinMax - we’re checking the type and assert if the size is between the min and max occurrence

    The resulting test would look more or less like this (note that we’re separating the autogenerated -assertions and the one from matchers with an and section):

    // given:
    +}

    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 +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 +defined in the response body, and passes the following check (based on the method being called):

    • For $.valueWithTypeMatch, the engine checks whether the type is the same.
    • For $.valueWithMin, the engine check the type and asserts whether the size is greater +than or equal to the minimum occurrence.
    • For $.valueWithMax, the engine checks the type and asserts whether the size is +smaller than or equal to the maximum occurrence.
    • For $.valueWithMinMax, the engine checks the type and asserts whether the size is +between the min and maximum occurrence.

    The resulting test would resemble the following example (note that an and section +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\"}");
    @@ -801,10 +857,11 @@ assertions and the one from matchers with an and se
      assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).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 we are calling the assertThatValueIsANumber. This method needs -to be defined in the test base class or should be statically imported to your tests. -Notice that the byCommand call was converted to assertThatValueIsANumber(parsedJson.read("$.duck"));. That means -that we took the method name and passed the proper JSON path as a parameter to it.

    and the WireMock stub like this:

    				'''
    + assertThatValueIsANumber(parsedJson.read("$.duck"));
    [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",
    @@ -854,9 +911,10 @@ that we took the method name and passed the proper JSON path as a parameter to i
         }
       }
     }
    -'''
    [Important]Important

    If you use a matcher then the part of the request / response that the matcher is addressing -via the JSON Path will get removed from assertion. In case of verifying a collection you have to create -matchers for all elements of the collection.

    Let’s look at the following example:

    Contract.make {
    +'''
    [Important]Important

    If you use a matcher, then the part of the request aned 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 {
         request {
             method 'GET'
             url("/foo")
    @@ -880,7 +938,7 @@ matchers for all elements of the co
                 jsonPath('$.events[0].status', byRegex('.+'))
             }
         }
    -}

    This will lead in creating the following test (showing just the assertion section)

    and:
    +}

    The preceding code leads to creating the following test (the code block shows only the assertion section):

    and:
     	DocumentContext parsedJson = JsonPath.parse(response.body.asString())
     	assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("16f1ed75-0bcc-4f0d-a04d-3121798faf99")
     	assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("EXPORT")
    @@ -890,9 +948,12 @@ matchers for all elements of the co
     and:
     	assertThat(parsedJson.read("\$.events[0].operation", String.class)).matches(".+")
     	assertThat(parsedJson.read("\$.events[0].eventId", String.class)).matches("^([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})\$")
    -	assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")

    As you can see the assertion is malformed. That’s because only the first element of the array got asserted. -In order to fix this it’s best to apply the assertion to the whole $.events collection and assert it -via the byCommand(…​) method.

7.7 JAX-RS support

We support JAX-RS 2 Client API. Base class needs to define protected WebTarget webTarget and server initialization, right now the only option how to test JAX-RS API is to start a web server.

Request with a body needs to have a content type set otherwise application/octet-stream is going to be used.

In order to use JAX-RS mode, use the following settings:

testMode == 'JAXRSCLIENT'

Example of a test API generated:

'''
+	assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")

As you can see, the assertion is malformed. Only the first element of the array got +asserted. In order to fix this, you should apply the assertion to the whole $.events +collection and assert it with the byCommand(…​) method.

7.6 JAX-RS Support

The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs +to define protected WebTarget webTarget and server initialization. The only option for +testing JAX-RS API is to start a web server. Also, a request with a body needs to have a +content type set. Otherwise, the default of application/octet-stream gets used.

In order to use JAX-RS mode, use the following settings:

testMode == 'JAXRSCLIENT'

The following example shows a generated test API:

'''
  // when:
   Response response = webTarget
     .path("/users")
@@ -914,9 +975,9 @@ via the byCommand(…​) method.

// and: DocumentContext parsedJson = JsonPath.parse(responseAsString); assertThatJson(parsedJson).field("['property1']").isEqualTo("a"); -'''

7.8 Async support

If you’re using asynchronous communication on the server side (your controllers are returning -Callable, DeferredResult etc. then inside your contract you have to provide in the response -section a async() method. Example:

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

7.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 {
     request {
         method GET()
         url '/get'
@@ -926,8 +987,10 @@ section a async() method. Example:

'Passed'
         async()
     }
-}

7.9 Working with Context Paths

Spring Cloud Contract supports context paths.

[Important]Important

The only thing that changes in order to fully support context paths is the switch -on the PRODUCER side. The autogenerated tests need to be using the EXPLICIT mode.

The consumer side remains untouched, in order for the generated test to pass you have to switch the EXPLICIT mode.

Maven.  +}

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

<plugin>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-contract-maven-plugin</artifactId>
@@ -941,8 +1004,9 @@ on the PRODUCER side. The autogener
 

contracts {
 		testMode = 'EXPLICIT'
 }

-

That way you’ll generate a test that DOES NOT use MockMvc. It means that you’re generating -real requests and you need to setup your generated test’s base class to work on a real socket.

Let’s imagine the following contract:

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

That way, you generate a test that DOES NOT use MockMvc. It means that you generate +real requests and you need to setup your generated test’s base class to work on a real +socket.

Consider the following contract:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'GET'
 		url '/my-context-path/url'
@@ -950,7 +1014,7 @@ real requests and you need to setup your generated test’s base class to wo
 	response {
 		status 200
 	}
-}

Here is an example of how to set up a base class and Rest Assured for everything to work correctly.

import com.jayway.restassured.RestAssured;
+}

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

import com.jayway.restassured.RestAssured;
 import org.junit.Before;
 import org.springframework.boot.context.embedded.LocalServerPort;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -965,8 +1029,11 @@ real requests and you need to setup your generated test’s base class to wo
 		RestAssured.baseURI = "http://localhost";
 		RestAssured.port = this.port;
 	}
-}

That way all:

  • all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. /my-context-path/url)
  • your contracts reflect that you have a context path, thus your generated stubs will also -have that information (e.g. in the stubs you’ll see that you have too call /my-context-path/url)

7.10 Messaging Top-Level Elements

The DSL for messaging looks a little bit different than the one that focuses on HTTP.

7.10.1 Output triggered by a method

The output message can be triggered by calling a method (e.g. a Scheduler was started and a message was sent)

def dsl = Contract.make {
+}

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).

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

7.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 {
 	// Human readable description
 	description 'Some description'
 	// Label by means of which the output message can be triggered
@@ -987,8 +1054,11 @@ have that information (e.g. in the stubs you’ll see that you have too call
 			header('BOOK-NAME', 'foo')
 		}
 	}
-}

In this case the output message will be sent to output if a method called bookReturnedTriggered will be executed. In the message publisher’s side -we will generate a test that will call that method to trigger the message. On the consumer side you can use the some_label to trigger the message.

7.10.2 Output triggered by a message

The output message can be triggered by receiving a message.

def dsl = Contract.make {
+}

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.

7.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 {
 	description 'Some Description'
 	label 'some_label'
 	// input is a message
@@ -1013,10 +1083,15 @@ we will generate a test that will call that method to trigger the message. On th
 			header('BOOK-NAME', 'foo')
 		}
 	}
-}

In this case the output message will be sent to output if a proper message will be received on the input destination. In the message publisher’s side -we will generate a test that will send the input message to the defined destination. On the consumer side you can either send a message to the input -destination or use the some_label to trigger the message.

7.10.3 Consumer / Producer

In HTTP you have a notion of client/stub and `server/test notation. You can use them also in messaging but we’re providing also the consumer and produer methods -as presented below (note you can use either $ or value methods to provide consumer and producer parts)

Contract.make {
+}

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.

7.9.3 Consumer/Producer

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 +parts):

Contract.make {
 	label 'some_label'
 	input {
 		messageFrom value(consumer('jms:output'), producer('jms:input'))
@@ -1033,7 +1108,11 @@ as presented below (note you can use either $ or 'foo'
 		])
 	}
-}

7.11 Multiple contracts in one file

It’s possible to define multiple contracts in one file. An example of such a contract can look like this

import org.springframework.cloud.contract.spec.Contract
+}

7.9.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.

7.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
 
 [
         Contract.make {
@@ -1055,8 +1134,8 @@ as presented below (note you can use either $ or 200
             }
         }
-]

In this example one contract has the name field and the other doesn’t. This will lead to generation of -two tests that will look more or less like this:

package org.springframework.cloud.contract.verifier.tests.com.hello;
+]

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;
 import com.jayway.jsonpath.DocumentContext;
@@ -1097,11 +1176,11 @@ two tests that will look more or less like this:

 			assertThat(response.statusCode()).isEqualTo(200);
 	}
 
-}

Notice that for the contract that has the name field the generated test method is named -validate_should_post_a_user. For the one that doesn’t have the name it’s called +}

Notice that, for the contract that has the name field, the generated test method is named +validate_should_post_a_user. For the one that does not have the name, it is called validate_withList_1. It corresponds to the name of the file WithList.groovy and the -index of the contract in the list.

The generated stubs will look like this

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 -contract had index 1 in the list of contracts in the file).

[Tip]Tip

As you can see it’s much better if you name your contracts since then your tests - are far more meaningful.

\ No newline at end of file +index of the contract in the list.

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 diff --git a/1.1.x/multi/multi__customization.html b/1.1.x/multi/multi__customization.html index 1f073657bb..36e9b9db44 100644 --- a/1.1.x/multi/multi__customization.html +++ b/1.1.x/multi/multi__customization.html @@ -1,8 +1,9 @@ - 8. Customization

8. Customization

8.1 Extending the DSL

It is possible to provide your own functions to the DSL. The key requirement for this -feature was to maintain the static compatibility. Below you will be able to see an example -of:

  • creation of a JAR with reusable classes
  • referencing of these classes in the DSLs

The full example can be found here.

8.1.1 Common JAR

Below you can find three classes that we will reuse in the DSLs.

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

package com.example;
+   8. Customization

8. Customization

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

8.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.

8.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;
 
 import java.util.regex.Pattern;
 
@@ -133,10 +134,10 @@ of:

    return new ServerDslProperty( PatternUtils.ok(), "OK"); } } -//end::impl[]

8.1.2 Adding the dependency to project

In order for the plugins and IDE to be able to reference the common JAR classes you need -to pass the dependency to your project.

8.1.3 Test dependency in project’s dependencies

First add the common jar dependency as a test dependency. That way since your -contracts files are available at test resources path, automatically the -common jar classes will be visible in your Groovy files.

Maven.  +//end::impl[]

8.1.2 Adding the Dependency to the Project

In order for the plugins and IDE to be able to reference the common JAR classes, you need +to pass the dependency to your project.

8.1.3 Test the Dependency in the Project’s Dependencies

First, add the common jar dependency as a test dependency. Because your contracts files +are available on the test resources path, the common jar classes automatically become +visible in your Groovy files. The following examples show how to test the dependency:

Maven. 

<dependency>
 	<groupId>com.example</groupId>
 	<artifactId>beer-common</artifactId>
@@ -145,7 +146,8 @@ common jar classes will be visible in your Groovy files.

< </dependency>

Gradle. 

testCompile("com.example:beer-common:0.0.1-SNAPSHOT")

-

8.1.4 Test dependency in plugin’s dependencies

Now you have to add the dependency for the plugin to reuse at runtime.

Maven.  +

8.1.4 Test a Dependency in the Plugin’s Dependencies

Now, you must add the dependency for the plugin to reuse at runtime, as shown in the +following example:

Maven. 

<plugin>
 	<groupId>org.springframework.cloud</groupId>
 	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
@@ -171,7 +173,7 @@ common jar classes will be visible in your Groovy files.

< </plugin>

Gradle. 

classpath "com.example:beer-common:0.0.1-SNAPSHOT"

-

8.1.5 Referencing classes in DSLs

Now you can reference your classes in your DSL. Example:

package contracts.beer.rest
+

8.1.5 Referencing classes in DSLs

You can now reference your classes in your DSL, as shown in the following example:

package contracts.beer.rest
 
 import com.example.ConsumerUtils
 import com.example.ProducerUtils
@@ -212,4 +214,4 @@ then:
 			contentType(applicationJson())
 		}
 	}
-}
\ No newline at end of file +} \ No newline at end of file diff --git a/1.1.x/multi/multi__spring_cloud_contract_wiremock.html b/1.1.x/multi/multi__spring_cloud_contract_wiremock.html index 7e316371ec..87b15af6ff 100644 --- a/1.1.x/multi/multi__spring_cloud_contract_wiremock.html +++ b/1.1.x/multi/multi__spring_cloud_contract_wiremock.html @@ -1,6 +1,6 @@ - 10. Spring Cloud Contract WireMock

10. Spring Cloud Contract WireMock

Modules giving you the possibility to use + 10. Spring Cloud Contract WireMock

10. Spring Cloud Contract WireMock

Modules giving you the possibility to use WireMock with different servers by using the "ambient" server embedded in a Spring Boot application. Check out the samples @@ -286,4 +286,4 @@ Contract.make { } } }

the generated document (example for Asciidoc) will contain a formatted contract -(the location of this file would be index/dsl-contract.adoc).

\ No newline at end of file +(the location of this file would be index/dsl-contract.adoc).

\ No newline at end of file diff --git a/1.1.x/multi/multi__using_the_pluggable_architecture.html b/1.1.x/multi/multi__using_the_pluggable_architecture.html new file mode 100644 index 0000000000..c83ccc33aa --- /dev/null +++ b/1.1.x/multi/multi__using_the_pluggable_architecture.html @@ -0,0 +1,461 @@ + + + 9. Using the Pluggable Architecture

9. Using the Pluggable Architecture

You may encounter cases where you have your contracts have been defined in other formats, +such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic +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).

9.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 +structure converter. The following code listing shows the ContractConverter interface:

package org.springframework.cloud.contract.spec
+
+/**
+ * Converter to be used to convert FROM {@link File} TO {@link Contract}
+ * and from {@link Contract} to {@code T}
+ *
+ * @param <T> - type to which we want to convert the contract
+ *
+ * @author Marcin Grzejszczak
+ * @since 1.1.0
+ */
+interface ContractConverter<T> {
+
+	/**
+	 * Should this file be accepted by the converter. Can use the file extension
+	 * to check if the conversion is possible.
+	 *
+	 * @param file - file to be considered for conversion
+	 * @return - {@code true} if the given implementation can convert the file
+	 */
+	boolean isAccepted(File file)
+
+	/**
+	 * Converts the given {@link File} to its {@link Contract} representation
+	 *
+	 * @param file - file to convert
+	 * @return - {@link Contract} representation of the file
+	 */
+	Collection<Contract> convertFrom(File file)
+
+	/**
+	 * Converts the given {@link Contract} to a {@link T} representation
+	 *
+	 * @param contract - the parsed contract
+	 * @return - {@link T} the type to which we do the conversion
+	 */
+	T convertTo(Collection<Contract> contract)
+}

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

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
+		}
+	}
+}

9.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.

9.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"
+  },
+  "consumer": {
+    "name": "Consumer"
+  },
+  "interactions": [
+    {
+      "description": "",
+      "request": {
+        "method": "PUT",
+        "path": "/fraudcheck",
+        "headers": {
+          "Content-Type": "application/vnd.fraud.v1+json"
+        },
+        "body": {
+          "clientId": "1234567890",
+          "loanAmount": 99999
+        },
+        "matchingRules": {
+          "$.body.clientId": {
+            "match": "regex",
+            "regex": "[0-9]{10}"
+          }
+        }
+      },
+      "response": {
+        "status": 200,
+        "headers": {
+          "Content-Type": "application/vnd.fraud.v1+json;charset=UTF-8"
+        },
+        "body": {
+          "fraudCheckStatus": "FRAUD",
+          "rejectionReason": "Amount too high"
+        },
+        "matchingRules": {
+          "$.body.fraudCheckStatus": {
+            "match": "regex",
+            "regex": "FRAUD"
+          }
+        }
+      }
+    }
+  ],
+  "metadata": {
+    "pact-specification": {
+      "version": "2.0.0"
+    },
+    "pact-jvm": {
+      "version": "2.4.18"
+    }
+  }
+}

The remainder of this section about using Pact refers to the preceding file.

9.1.3 Pact for Producers

On the producer side, you mustadd 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>
+	<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.fraud</packageWithBaseClasses>
+	</configuration>
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.cloud</groupId>
+			<artifactId>spring-cloud-contract-spec-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'

+

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 {
+	// given:
+		MockMvcRequestSpecification request = given()
+				.header("Content-Type", "application/vnd.fraud.v1+json")
+				.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
+
+	// when:
+		ResponseOptions response = given().spec(request)
+				.put("/fraudcheck");
+
+	// then:
+		assertThat(response.statusCode()).isEqualTo(200);
+		assertThat(response.header("Content-Type")).isEqualTo("application/vnd.fraud.v1+json;charset=UTF-8");
+	// and:
+		DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
+		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:

{
+  "uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
+  "request" : {
+    "url" : "/fraudcheck",
+    "method" : "PUT",
+    "headers" : {
+      "Content-Type" : {
+        "equalTo" : "application/vnd.fraud.v1+json"
+      }
+    },
+    "bodyPatterns" : [ {
+      "matchesJsonPath" : "$[?(@.loanAmount == 99999)]"
+    }, {
+      "matchesJsonPath" : "$[?(@.clientId =~ /([0-9]{10})/)]"
+    } ]
+  },
+  "response" : {
+    "status" : 200,
+    "body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
+    "headers" : {
+      "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
+    }
+  }
+}

9.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>
+	<scope>test</scope>
+</dependency>

+

Gradle.  +

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

+

9.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
+
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+/**
+ * Builds a single test.
+ *
+ * @since 1.1.0
+ */
+interface SingleTestGenerator {
+
+	/**
+	 * Creates contents of a single test class in which all test scenarios from
+	 * the contract metadata should be placed.
+	 *
+	 * @param properties - properties passed to the plugin
+	 * @param listOfFiles - list of parsed contracts with additional metadata
+	 * @param className - the name of the generated test class
+	 * @param classPackage - the name of the package in which the test class should be stored
+	 * @param includedDirectoryRelativePath - relative path to the included directory
+	 * @return contents of a single test class
+	 */
+	String buildClass(ContractVerifierConfigProperties properties, Collection<ContractMetadata> listOfFiles,
+					  String className, String classPackage, String includedDirectoryRelativePath)
+
+	/**
+	 * Extension that should be appended to the generated test class. E.g. {@code .java} or {@code .php}
+	 *
+	 * @param properties - properties passed to the plugin
+	 */
+	String fileExtension(ContractVerifierConfigProperties properties)
+}

Again, you must provide a spring.factories file, such as the one shown in the following +example:

org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
+com.example.MyGenerator

9.3 Using the Custom Stub Generator

If you want to generate stubs for stub servers other than WireMock, you can plug in your +own implementation of the StubGenerator interface. The following code listing shows the +StubGenerator interface:

package org.springframework.cloud.contract.verifier.converter
+
+import groovy.transform.CompileStatic
+import org.springframework.cloud.contract.spec.Contract
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+
+/**
+ * Converts contracts into their stub representation.
+ *
+ * @since 1.1.0
+ */
+@CompileStatic
+interface StubGenerator {
+
+	/**
+	 * Returns {@code true} if the converter can handle the file to convert it into a stub.
+	 */
+	boolean canHandleFileName(String fileName)
+
+	/**
+	 * Returns the collection of converted contracts into stubs. One contract can
+	 * result in multiple stubs.
+	 */
+	Map<Contract, String> convertContents(String rootName, ContractMetadata content)
+
+	/**
+	 * Returns the name of the converted stub file. If you have multiple contracts
+	 * in a single file then a prefix will be added to the generated file. If you
+	 * provide the {@link Contract#name} field then that field will override the
+	 * generated file name.
+	 *
+	 * Example: name of file with 2 contracts is {@code foo.groovy}, it will be
+	 * converted by the implementation to {@code foo.json}. The recursive file
+	 * converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
+	 */
+	String generateOutputFileNameForInput(String inputFileName)
+}

Again, you must provide a spring.factories file, such as the one shown in the following +example:

# Stub converters
+org.springframework.cloud.contract.verifier.converter.StubGenerator=\
+org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

The default implementation is the WireMock stub generation.

[Tip]Tip

You can provide multiple stub generator implementations. For example, from a single +DSL, you can produce both WireMock stubs and Pact files.

9.4 Using the Custom Stub Runner

If you decide to use a custom stub generation, you also need a custom way of running +stubs with your different stub provider.

Assume that you use Moco to build your stubs and that +you have written a stub generator and placed your stubs in a JAR file.

In order for Stub Runner to know how to run your stubs, you have to define a custom +HTTP Stub server implementation, which might resemble the following example:

package org.springframework.cloud.contract.stubrunner.provider.moco
+
+import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
+import com.github.dreamhead.moco.runner.JsonRunner
+import com.github.dreamhead.moco.runner.RunnerSetting
+import groovy.util.logging.Slf4j
+import org.springframework.cloud.contract.stubrunner.HttpServerStub
+import org.springframework.util.SocketUtils
+
+@Slf4j
+class MocoHttpServerStub implements HttpServerStub {
+
+	private boolean started
+	private JsonRunner runner
+	private int port
+
+	@Override
+	int port() {
+		if (!isRunning()) {
+			return -1
+		}
+		return port
+	}
+
+	@Override
+	boolean isRunning() {
+		return started
+	}
+
+	@Override
+	HttpServerStub start() {
+		return start(SocketUtils.findAvailableTcpPort())
+	}
+
+	@Override
+	HttpServerStub start(int port) {
+		this.port = port
+		return this
+	}
+
+	@Override
+	HttpServerStub stop() {
+		if (!isRunning()) {
+			return this
+		}
+		this.runner.stop()
+		return this
+	}
+
+	@Override
+	HttpServerStub registerMappings(Collection<File> stubFiles) {
+		List<RunnerSetting> settings = stubFiles.findAll { it.name.endsWith("json") }
+				.collect {
+			log.info("Trying to parse [{}]", it.name)
+			try {
+				return RunnerSetting.aRunnerSetting().withStream(it.newInputStream()).build()
+			} catch (Exception e) {
+				log.warn("Exception occurred while trying to parse file [{}]", it.name, e)
+				return null
+			}
+		}.findAll { it }
+		this.runner = JsonRunner.newJsonRunnerWithSetting(settings,
+				HttpArgs.httpArgs().withPort(this.port).build())
+		this.runner.run()
+		this.started = true
+		return this
+	}
+
+	@Override
+	boolean isAccepted(File file) {
+		return file.name.endsWith(".json")
+	}
+}

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

org.springframework.cloud.contract.stubrunner.HttpServerStub=\
+org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub

Now you can run stubs with Moco.

[Important]Important

If you do not provide any implementation, then the default (WireMock) +implementation is used. If you provide more than one, the first one on the list is used.

9.5 Using the Custom Stub Downloader

You can customize the way your stubs are downloaded by creating an implementation of the +StubDownloaderBuilder interface, as shown in the following example:

package com.example;
+
+class CustomStubDownloaderBuilder implements StubDownloaderBuilder {
+
+	@Override
+	public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
+		return new StubDownloader() {
+			@Override
+			public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
+					StubConfiguration config) {
+				File unpackedStubs = retrieveStubs();
+				return new AbstractMap.SimpleEntry<>(
+						new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
+								config.getClassifier()), unpackedStubs);
+			}
+
+			File retrieveStubs() {
+			    // here goes your custom logic to provide a folder where all the stubs reside
+			}
+}

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 diff --git a/1.1.x/multi/multi_spring-cloud-contract.html b/1.1.x/multi/multi_spring-cloud-contract.html index a5c2d64f29..d82031d0c2 100644 --- a/1.1.x/multi/multi_spring-cloud-contract.html +++ b/1.1.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?
2.1.1. Testing issues
2.2. Purposes
2.3. How
2.3.1. Define the contract
2.3.2. Client Side
2.3.3. Server Side
2.4. Step by step guide to 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 Verifier Setup
3.1. Gradle Project
3.1.1. Prerequisites
3.1.2. Add gradle plugin with dependencies
3.1.3. Gradle and Rest Assured 3.0
3.1.4. Snapshot versions for Gradle
3.1.5. Add stubs
3.1.6. Run plugin
3.1.7. Default setup
3.1.8. Configure plugin
3.1.9. Configuration options
3.1.10. Single base class for all tests
3.1.11. Different base classes for contracts
3.1.12. Invoking generated tests
3.1.13. Spring Cloud Contract Verifier on consumer side
3.2. Using in your Maven project
3.2.1. Add maven plugin
3.2.2. Maven and Rest Assured 3.0
3.2.3. Snapshot versions for Maven
3.2.4. Add stubs
3.2.5. Run plugin
3.2.6. Configure plugin
3.2.7. Important configuration options
3.2.8. Single base class for all tests
3.2.9. Different base classes for contracts
3.2.10. Invoking generated tests
3.2.11. FAQ with Maven Plugin
3.2.12. Maven Plugin and STS
3.2.13. Spring Cloud Contract Verifier on consumer side
3.3. Scenarios
3.4. Stubs and transitive dependencies
4. Spring Cloud Contract Verifier Messaging
4.1. Integrations
4.2. Manual Integration Testing
4.3. Publisher side test generation
4.3.1. Scenario 1 (no input message)
4.3.2. Scenario 2 (output triggered by input)
4.3.3. Scenario 3 (no output message)
4.4. Consumer Stub Side generation
5. Spring Cloud Contract Stub Runner
5.1. Snapshot versions
5.2. Publishing stubs as JARs
5.3. Stub Runner Core
5.3.1. Retrieving stubs
Stub downloading
Classpath scanning
5.3.2. Running stubs
Limitations
Running using main app
HTTP Stubs
Viewing registered mappings
Messaging Stubs
5.4. Stub Runner JUnit Rule
5.4.1. Maven settings
5.4.2. Providing fixed ports
5.4.3. Fluent API
5.4.4. Stub Runner with Spring
5.5. Stub Runner Spring Cloud
5.5.1. Stubbing Service Discovery
Test profiles and service discovery
5.5.2. Additional Configuration
5.6. Stub Runner Boot Application
5.6.1. How to use it?
Stub Runner Server
Spring Cloud CLI
5.6.2. Endpoints
HTTP
Messaging
5.6.3. Example
5.6.4. Stub Runner Boot with Service Discovery
5.7. Stubs Per Consumer
5.8. Common
5.8.1. Common properties for JUnit and Spring
5.8.2. Stub runner stubs ids
6. Stub Runner for Messaging
6.1. Stub triggering
6.1.1. Trigger by label
6.1.2. Trigger by group and artifact ids
6.1.3. Trigger by artifact ids
6.1.4. Trigger all messages
6.2. Stub Runner Camel
6.2.1. Adding it to the project
6.2.2. Disabling the functionality
6.2.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.3. Stub Runner Integration
6.3.1. Adding it to the project
6.3.2. Disabling the functionality
6.3.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.4. Stub Runner Stream
6.4.1. Adding it to the project
6.4.2. Disabling the functionality
6.4.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.5. Stub Runner Spring AMQP
6.5.1. Adding it to the project
6.5.2. Examples
Stubs structure
Triggering the message
Spring AMQP Test Configuration
7. Contract DSL
7.1. Limitations
7.2. Common Top-Level elements
7.2.1. Description
7.2.2. Name
7.2.3. Ignoring contracts
7.3. HTTP Top-Level Elements
7.4. Request
7.5. Response
7.6. Dynamic properties
7.6.1. Dynamic properties inside the body
7.6.2. Regular expressions
7.6.3. Passing optional parameters
7.6.4. Executing custom methods on server side
7.6.5. Referencing request from response
7.6.6. Dynamic properties in matchers sections
7.7. JAX-RS support
7.8. Async support
7.9. Working with Context Paths
7.10. Messaging Top-Level Elements
7.10.1. Output triggered by a method
7.10.2. Output triggered by a message
7.10.3. Consumer / Producer
7.11. Multiple contracts in one file
8. Customization
8.1. Extending the DSL
8.1.1. Common JAR
8.1.2. Adding the dependency to project
8.1.3. Test dependency in project’s dependencies
8.1.4. Test dependency in plugin’s dependencies
8.1.5. Referencing classes in DSLs
9. Pluggable architecture
9.1. Custom contract converter
9.1.1. Pact converter
9.1.2. Pact contract
9.1.3. Pact for producers
9.1.4. Pact for consumers
9.2. Custom test generator
9.3. Custom stub generator
9.4. Custom Stub Runner
9.5. Custom Stub Downloader
10. Spring Cloud Contract WireMock
10.1. Registering Stubs Automatically
10.2. Using Files to Specify the Stub Bodies
10.3. Alternative: Using JUnit Rules
10.4. Relaxed SSL Validation for Rest Template
10.5. WireMock and Spring MVC Mocks
10.6. Generating Stubs using RestDocs
10.7. Generating Contracts using RestDocs
11. 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?
2.1.1. Testing issues
2.2. Purposes
2.3. How
2.3.1. Define the contract
2.3.2. Client Side
2.3.3. Server Side
2.4. Step by step guide to 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 Verifier Setup
3.1. Gradle Project
3.1.1. Prerequisites
3.1.2. Add gradle plugin with dependencies
3.1.3. Gradle and Rest Assured 3.0
3.1.4. Snapshot versions for Gradle
3.1.5. Add stubs
3.1.6. Run plugin
3.1.7. Default setup
3.1.8. Configure plugin
3.1.9. Configuration options
3.1.10. Single base class for all tests
3.1.11. Different base classes for contracts
3.1.12. Invoking generated tests
3.1.13. Spring Cloud Contract Verifier on consumer side
3.2. Using in your Maven project
3.2.1. Add maven plugin
3.2.2. Maven and Rest Assured 3.0
3.2.3. Snapshot versions for Maven
3.2.4. Add stubs
3.2.5. Run plugin
3.2.6. Configure plugin
3.2.7. Important configuration options
3.2.8. Single base class for all tests
3.2.9. Different base classes for contracts
3.2.10. Invoking generated tests
3.2.11. FAQ with Maven Plugin
3.2.12. Maven Plugin and STS
3.2.13. Spring Cloud Contract Verifier on consumer side
3.3. Scenarios
3.4. Stubs and transitive dependencies
4. Spring Cloud Contract Verifier Messaging
4.1. Integrations
4.2. Manual Integration Testing
4.3. Publisher side test generation
4.3.1. Scenario 1 (no input message)
4.3.2. Scenario 2 (output triggered by input)
4.3.3. Scenario 3 (no output message)
4.4. Consumer Stub Side generation
5. Spring Cloud Contract Stub Runner
5.1. Snapshot versions
5.2. Publishing stubs as JARs
5.3. Stub Runner Core
5.3.1. Retrieving stubs
Stub downloading
Classpath scanning
5.3.2. Running stubs
Limitations
Running using main app
HTTP Stubs
Viewing registered mappings
Messaging Stubs
5.4. Stub Runner JUnit Rule
5.4.1. Maven settings
5.4.2. Providing fixed ports
5.4.3. Fluent API
5.4.4. Stub Runner with Spring
5.5. Stub Runner Spring Cloud
5.5.1. Stubbing Service Discovery
Test profiles and service discovery
5.5.2. Additional Configuration
5.6. Stub Runner Boot Application
5.6.1. How to use it?
Stub Runner Server
Spring Cloud CLI
5.6.2. Endpoints
HTTP
Messaging
5.6.3. Example
5.6.4. Stub Runner Boot with Service Discovery
5.7. Stubs Per Consumer
5.8. Common
5.8.1. Common properties for JUnit and Spring
5.8.2. Stub runner stubs ids
6. Stub Runner for Messaging
6.1. Stub triggering
6.1.1. Trigger by label
6.1.2. Trigger by group and artifact ids
6.1.3. Trigger by artifact ids
6.1.4. Trigger all messages
6.2. Stub Runner Camel
6.2.1. Adding it to the project
6.2.2. Disabling the functionality
6.2.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.3. Stub Runner Integration
6.3.1. Adding it to the project
6.3.2. Disabling the functionality
6.3.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.4. Stub Runner Stream
6.4.1. Adding it to the project
6.4.2. Disabling the functionality
6.4.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.5. Stub Runner Spring AMQP
6.5.1. Adding it to the project
6.5.2. Examples
Stubs structure
Triggering the message
Spring AMQP Test Configuration
7. Contract DSL
7.1. Limitations
7.2. Common Top-Level elements
7.2.1. Description
7.2.2. Name
7.2.3. Ignoring Contracts
7.2.4. Passing Values from Files
7.2.5. HTTP Top-Level Elements
7.3. Request
7.4. Response
7.5. Dynamic properties
7.5.1. Dynamic properties inside the body
7.5.2. Regular expressions
7.5.3. Passing Optional Parameters
7.5.4. Executing Custom Methods on the Server Side
7.5.5. Referencing the Request from the Response
7.5.6. Registering Your Own WireMock Extension
7.5.7. Dynamic Properties in the Matchers Sections
7.6. JAX-RS Support
7.7. Async Support
7.8. Working with Context Paths
7.9. Messaging Top-Level Elements
7.9.1. Output Triggered by a Method
7.9.2. Output Triggered by a Message
7.9.3. Consumer/Producer
7.9.4. Common
7.10. Multiple Contracts in One File
8. Customization
8.1. Extending the DSL
8.1.1. Common JAR
8.1.2. Adding the Dependency to the Project
8.1.3. Test the Dependency in the Project’s Dependencies
8.1.4. Test a Dependency in the Plugin’s Dependencies
8.1.5. Referencing classes in DSLs
9. Using the Pluggable Architecture
9.1. Custom Contract Converter
9.1.1. Pact Converter
9.1.2. Pact Contract
9.1.3. Pact for Producers
9.1.4. Pact for Consumers
9.2. Using the Custom Test Generator
9.3. Using the Custom Stub Generator
9.4. Using the Custom Stub Runner
9.5. Using the Custom Stub Downloader
10. Spring Cloud Contract WireMock
10.1. Registering Stubs Automatically
10.2. Using Files to Specify the Stub Bodies
10.3. Alternative: Using JUnit Rules
10.4. Relaxed SSL Validation for Rest Template
10.5. WireMock and Spring MVC Mocks
10.6. Generating Stubs using RestDocs
10.7. Generating Contracts using RestDocs
11. Links
\ No newline at end of file diff --git a/1.1.x/single/spring-cloud-contract.html b/1.1.x/single/spring-cloud-contract.html index d81431573d..1efa28c3a2 100644 --- a/1.1.x/single/spring-cloud-contract.html +++ b/1.1.x/single/spring-cloud-contract.html @@ -1,6 +1,6 @@ - Spring Cloud Contract

Spring Cloud Contract


Table of Contents

1. Spring Cloud Contract
2. Spring Cloud Contract Verifier Introduction
2.1. Why?
2.1.1. Testing issues
2.2. Purposes
2.3. How
2.3.1. Define the contract
2.3.2. Client Side
2.3.3. Server Side
2.4. Step by step guide to 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 Verifier Setup
3.1. Gradle Project
3.1.1. Prerequisites
3.1.2. Add gradle plugin with dependencies
3.1.3. Gradle and Rest Assured 3.0
3.1.4. Snapshot versions for Gradle
3.1.5. Add stubs
3.1.6. Run plugin
3.1.7. Default setup
3.1.8. Configure plugin
3.1.9. Configuration options
3.1.10. Single base class for all tests
3.1.11. Different base classes for contracts
3.1.12. Invoking generated tests
3.1.13. Spring Cloud Contract Verifier on consumer side
3.2. Using in your Maven project
3.2.1. Add maven plugin
3.2.2. Maven and Rest Assured 3.0
3.2.3. Snapshot versions for Maven
3.2.4. Add stubs
3.2.5. Run plugin
3.2.6. Configure plugin
3.2.7. Important configuration options
3.2.8. Single base class for all tests
3.2.9. Different base classes for contracts
3.2.10. Invoking generated tests
3.2.11. FAQ with Maven Plugin
3.2.12. Maven Plugin and STS
3.2.13. Spring Cloud Contract Verifier on consumer side
3.3. Scenarios
3.4. Stubs and transitive dependencies
4. Spring Cloud Contract Verifier Messaging
4.1. Integrations
4.2. Manual Integration Testing
4.3. Publisher side test generation
4.3.1. Scenario 1 (no input message)
4.3.2. Scenario 2 (output triggered by input)
4.3.3. Scenario 3 (no output message)
4.4. Consumer Stub Side generation
5. Spring Cloud Contract Stub Runner
5.1. Snapshot versions
5.2. Publishing stubs as JARs
5.3. Stub Runner Core
5.3.1. Retrieving stubs
Stub downloading
Classpath scanning
5.3.2. Running stubs
Limitations
Running using main app
HTTP Stubs
Viewing registered mappings
Messaging Stubs
5.4. Stub Runner JUnit Rule
5.4.1. Maven settings
5.4.2. Providing fixed ports
5.4.3. Fluent API
5.4.4. Stub Runner with Spring
5.5. Stub Runner Spring Cloud
5.5.1. Stubbing Service Discovery
Test profiles and service discovery
5.5.2. Additional Configuration
5.6. Stub Runner Boot Application
5.6.1. How to use it?
Stub Runner Server
Spring Cloud CLI
5.6.2. Endpoints
HTTP
Messaging
5.6.3. Example
5.6.4. Stub Runner Boot with Service Discovery
5.7. Stubs Per Consumer
5.8. Common
5.8.1. Common properties for JUnit and Spring
5.8.2. Stub runner stubs ids
6. Stub Runner for Messaging
6.1. Stub triggering
6.1.1. Trigger by label
6.1.2. Trigger by group and artifact ids
6.1.3. Trigger by artifact ids
6.1.4. Trigger all messages
6.2. Stub Runner Camel
6.2.1. Adding it to the project
6.2.2. Disabling the functionality
6.2.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.3. Stub Runner Integration
6.3.1. Adding it to the project
6.3.2. Disabling the functionality
6.3.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.4. Stub Runner Stream
6.4.1. Adding it to the project
6.4.2. Disabling the functionality
6.4.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.5. Stub Runner Spring AMQP
6.5.1. Adding it to the project
6.5.2. Examples
Stubs structure
Triggering the message
Spring AMQP Test Configuration
7. Contract DSL
7.1. Limitations
7.2. Common Top-Level elements
7.2.1. Description
7.2.2. Name
7.2.3. Ignoring contracts
7.3. HTTP Top-Level Elements
7.4. Request
7.5. Response
7.6. Dynamic properties
7.6.1. Dynamic properties inside the body
7.6.2. Regular expressions
7.6.3. Passing optional parameters
7.6.4. Executing custom methods on server side
7.6.5. Referencing request from response
7.6.6. Dynamic properties in matchers sections
7.7. JAX-RS support
7.8. Async support
7.9. Working with Context Paths
7.10. Messaging Top-Level Elements
7.10.1. Output triggered by a method
7.10.2. Output triggered by a message
7.10.3. Consumer / Producer
7.11. Multiple contracts in one file
8. Customization
8.1. Extending the DSL
8.1.1. Common JAR
8.1.2. Adding the dependency to project
8.1.3. Test dependency in project’s dependencies
8.1.4. Test dependency in plugin’s dependencies
8.1.5. Referencing classes in DSLs
9. Pluggable architecture
9.1. Custom contract converter
9.1.1. Pact converter
9.1.2. Pact contract
9.1.3. Pact for producers
9.1.4. Pact for consumers
9.2. Custom test generator
9.3. Custom stub generator
9.4. Custom Stub Runner
9.5. Custom Stub Downloader
10. Spring Cloud Contract WireMock
10.1. Registering Stubs Automatically
10.2. Using Files to Specify the Stub Bodies
10.3. Alternative: Using JUnit Rules
10.4. Relaxed SSL Validation for Rest Template
10.5. WireMock and Spring MVC Mocks
10.6. Generating Stubs using RestDocs
10.7. Generating Contracts using RestDocs
11. Links

Documentation Authors: Adam Dudczak, Mathias Düsterhöft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubryński, Karol Lassak, + Spring Cloud Contract

Spring Cloud Contract


Table of Contents

1. Spring Cloud Contract
2. Spring Cloud Contract Verifier Introduction
2.1. Why?
2.1.1. Testing issues
2.2. Purposes
2.3. How
2.3.1. Define the contract
2.3.2. Client Side
2.3.3. Server Side
2.4. Step by step guide to 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 Verifier Setup
3.1. Gradle Project
3.1.1. Prerequisites
3.1.2. Add gradle plugin with dependencies
3.1.3. Gradle and Rest Assured 3.0
3.1.4. Snapshot versions for Gradle
3.1.5. Add stubs
3.1.6. Run plugin
3.1.7. Default setup
3.1.8. Configure plugin
3.1.9. Configuration options
3.1.10. Single base class for all tests
3.1.11. Different base classes for contracts
3.1.12. Invoking generated tests
3.1.13. Spring Cloud Contract Verifier on consumer side
3.2. Using in your Maven project
3.2.1. Add maven plugin
3.2.2. Maven and Rest Assured 3.0
3.2.3. Snapshot versions for Maven
3.2.4. Add stubs
3.2.5. Run plugin
3.2.6. Configure plugin
3.2.7. Important configuration options
3.2.8. Single base class for all tests
3.2.9. Different base classes for contracts
3.2.10. Invoking generated tests
3.2.11. FAQ with Maven Plugin
3.2.12. Maven Plugin and STS
3.2.13. Spring Cloud Contract Verifier on consumer side
3.3. Scenarios
3.4. Stubs and transitive dependencies
4. Spring Cloud Contract Verifier Messaging
4.1. Integrations
4.2. Manual Integration Testing
4.3. Publisher side test generation
4.3.1. Scenario 1 (no input message)
4.3.2. Scenario 2 (output triggered by input)
4.3.3. Scenario 3 (no output message)
4.4. Consumer Stub Side generation
5. Spring Cloud Contract Stub Runner
5.1. Snapshot versions
5.2. Publishing stubs as JARs
5.3. Stub Runner Core
5.3.1. Retrieving stubs
Stub downloading
Classpath scanning
5.3.2. Running stubs
Limitations
Running using main app
HTTP Stubs
Viewing registered mappings
Messaging Stubs
5.4. Stub Runner JUnit Rule
5.4.1. Maven settings
5.4.2. Providing fixed ports
5.4.3. Fluent API
5.4.4. Stub Runner with Spring
5.5. Stub Runner Spring Cloud
5.5.1. Stubbing Service Discovery
Test profiles and service discovery
5.5.2. Additional Configuration
5.6. Stub Runner Boot Application
5.6.1. How to use it?
Stub Runner Server
Spring Cloud CLI
5.6.2. Endpoints
HTTP
Messaging
5.6.3. Example
5.6.4. Stub Runner Boot with Service Discovery
5.7. Stubs Per Consumer
5.8. Common
5.8.1. Common properties for JUnit and Spring
5.8.2. Stub runner stubs ids
6. Stub Runner for Messaging
6.1. Stub triggering
6.1.1. Trigger by label
6.1.2. Trigger by group and artifact ids
6.1.3. Trigger by artifact ids
6.1.4. Trigger all messages
6.2. Stub Runner Camel
6.2.1. Adding it to the project
6.2.2. Disabling the functionality
6.2.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.3. Stub Runner Integration
6.3.1. Adding it to the project
6.3.2. Disabling the functionality
6.3.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.4. Stub Runner Stream
6.4.1. Adding it to the project
6.4.2. Disabling the functionality
6.4.3. Examples
Stubs structure
Scenario 1 (no input message)
Scenario 2 (output triggered by input)
Scenario 3 (input with no output)
6.5. Stub Runner Spring AMQP
6.5.1. Adding it to the project
6.5.2. Examples
Stubs structure
Triggering the message
Spring AMQP Test Configuration
7. Contract DSL
7.1. Limitations
7.2. Common Top-Level elements
7.2.1. Description
7.2.2. Name
7.2.3. Ignoring Contracts
7.2.4. Passing Values from Files
7.2.5. HTTP Top-Level Elements
7.3. Request
7.4. Response
7.5. Dynamic properties
7.5.1. Dynamic properties inside the body
7.5.2. Regular expressions
7.5.3. Passing Optional Parameters
7.5.4. Executing Custom Methods on the Server Side
7.5.5. Referencing the Request from the Response
7.5.6. Registering Your Own WireMock Extension
7.5.7. Dynamic Properties in the Matchers Sections
7.6. JAX-RS Support
7.7. Async Support
7.8. Working with Context Paths
7.9. Messaging Top-Level Elements
7.9.1. Output Triggered by a Method
7.9.2. Output Triggered by a Message
7.9.3. Consumer/Producer
7.9.4. Common
7.10. Multiple Contracts in One File
8. Customization
8.1. Extending the DSL
8.1.1. Common JAR
8.1.2. Adding the Dependency to the Project
8.1.3. Test the Dependency in the Project’s Dependencies
8.1.4. Test a Dependency in the Plugin’s Dependencies
8.1.5. Referencing classes in DSLs
9. Using the Pluggable Architecture
9.1. Custom Contract Converter
9.1.1. Pact Converter
9.1.2. Pact Contract
9.1.3. Pact for Producers
9.1.4. Pact for Consumers
9.2. Using the Custom Test Generator
9.3. Using the Custom Stub Generator
9.4. Using the Custom Stub Runner
9.5. Using the Custom Stub Downloader
10. Spring Cloud Contract WireMock
10.1. Registering Stubs Automatically
10.2. Using Files to Specify the Stub Bodies
10.3. Alternative: Using JUnit Rules
10.4. Relaxed SSL Validation for Rest Template
10.5. WireMock and Spring MVC Mocks
10.6. Generating Stubs using RestDocs
10.7. Generating Contracts using RestDocs
11. 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

1.1.5.BUILD-SNAPSHOT

1. Spring Cloud Contract

What you always need is confidence in pushing new features into a new application or service in a distributed system. This project provides support for Consumer Driven Contracts and service schemas in Spring applications, covering a range of options for writing tests, publishing them as assets, asserting that a contract is kept by producers @@ -2021,12 +2021,16 @@ So the following listener definition is a match and is invoked with the contract this.person = person; }

[Note]Note

The message is directly handed over to the onMessage method of the MessageListener associated with the matching SimpleMessageListenerContainer.

Spring AMQP Test Configuration

In order to avoid that Spring AMQP is trying to connect to a running broker during our tests we configure a mock ConnectionFactory.

To disable the mocked ConnectionFactory set the property stubrunner.amqp.mockConnection=false

stubrunner:
   amqp:
-    mockConnection: false

7. Contract DSL

[Important]Important

Remember that inside the contract file you have to provide the fully qualified name to -the Contract class and the make static import i.e. 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 don’t be alarmed if you didn’t use Groovy before. Knowledge of the language is not really needed as our DSL uses only -a tiny subset of it (namely literals, method calls and closures). What’s more the DSL is designed to be programmer-readable without any knowledge of the DSL itself - - it’s statically typed.

[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.

Let’s look at full example of a contract definition.

org.springframework.cloud.contract.spec.Contract.make {
+    mockConnection: false

7. Contract DSL

[Important]Important

Remember that, inside the 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 {
 	request {
 		method 'PUT'
 		url '/api/12'
@@ -2067,11 +2071,16 @@ a tiny subset of it (namely literals, method calls and closures). What’s m
 	response {
 		status 200
 	}
-}

Not all features of the DSL are used in example above. If you didn’t find what you are looking for, please check next paragraphs on this page.

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

7.1 Limitations

[Warning]Warning

Spring Cloud Contract Verifier doesn’t support XML properly. Please use JSON or help us implement this feature.

[Warning]Warning

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

[Warning]Warning

Due to the fact that JSON structure can have any form it’s sometimes impossible to parse it properly when using -the value(consumer(…​), producer(…​)) notation when using that in GString. That’s why we highly recommend using the -Groovy Map notation.

7.2 Common Top-Level elements

7.2.1 Description

You can add a description to your contract that is nothing else but an arbitrary text. Example:

		org.springframework.cloud.contract.spec.Contract.make {
+}
[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

7.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 +is why you should use the Groovy Map notation.

7.2 Common Top-Level elements

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

7.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 {
 			description('''
 given:
 	An input
@@ -2080,14 +2089,27 @@ when:
 then:
 	Output
 ''')
-		}

7.2.2 Name

You can provide a name of your contract. Let’s assume that you’ve provided a name should register a user. -If you do this then the name of the autogenerated test will be equal to validate_should_register_a_user. -Also the name of the stub will be should_register_a_user.json in case of a WireMock stub.

[Important]Important

Please ensure that the name doesn’t contain any characters that will make the generated test - not possible to compile. Also remember that if you provide the same name for multiple contracts then your - autogenerated tests will fail to compile and your generated stubs will override each other.

7.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 just set the ignored property on the contract itself:

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

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

7.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 {
 	ignored()
-}

7.3 HTTP Top-Level Elements

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 {
+}

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

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/readFromFile.groovy[indent=0]

Further assume that the JSON files is as follows:

request.json

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/request.json[indent=0]

response.json

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/response.json[indent=0]

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.

7.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 {
 	// Definition of HTTP request part of the contract
 	// (this can be a valid request or invalid depending
 	// on type of contract being specified).
@@ -2106,7 +2128,8 @@ or just set the ignored property on the contract it
 	// Contract priority, which can be used for overriding
 	// contracts (1 is highest). Priority is optional.
 	priority 1
-}

7.4 Request

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 {
+}

7.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 {
 	request {
 		// HTTP request method (GET/POST/PUT/DELETE).
 		method 'GET'
@@ -2118,7 +2141,8 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

It is possible to specify whole url instead of just path, but urlPath is the recommended way as it makes the tests host-independent.

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

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 {
 	request {
 		method 'GET'
 
@@ -2129,7 +2153,8 @@ or just set the ignored property on the contract it
 	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 {
+}

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 {
 	request {
 		//...
 
@@ -2169,7 +2194,7 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

It may contain additional request headers…​

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

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

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -2186,7 +2211,7 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

…​and a request body.

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

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

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		//...
 
@@ -2198,7 +2223,8 @@ or just set the ignored property on the contract it
 	response {
 		//...
 	}
-}

Request may contain multipart elements. Just call the multipart() method.

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

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 {
 	request {
 		method "PUT"
 		url "/multipart"
@@ -2221,12 +2247,11 @@ or just set the ignored property on the contract it
 	response {
 		status 200
 	}
-}

In this example we defined parameters either directly by using the map notation, -where the value can be a dynamic property (e.g. formParameter: $(consumer(…​), producer(…​))) - or by using the named(…​) method that allows you to set a named parameter. - A named parameter can set a name and content. You can call it either via - a method with 2 arguments: e.g. named("fileName", "fileContent") or - via a map notation named(name: "fileName", content: "fileContent").

From this contract the generated test will look more or less like this:

// given:
+}

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 +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:
  MockMvcRequestSpecification request = given()
    .header("Content-Type", "multipart/form-data;boundary=AaB03x")
    .param("formParameter", "\"formParameterValue\"")
@@ -2238,7 +2263,7 @@ where the value can be a dynamic property (e.g. formParame
    .put("/multipart");
 
 // then:
- assertThat(response.statusCode()).isEqualTo(200);

The WireMock stub will look more or less like this:

			'''
+ assertThat(response.statusCode()).isEqualTo(200);

The WireMock stub is as follows:

			'''
 {
   "request" : {
 	"url" : "/multipart",
@@ -2261,7 +2286,8 @@ where the value can be a dynamic property (e.g. formParame
 	"transformers" : [ "response-template" ]
   }
 }
-	'''

7.5 Response

Minimal response must contain HTTP status code.

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

7.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 {
 	request {
 		//...
 	}
@@ -2270,19 +2296,24 @@ where the value can be a dynamic property (e.g. formParame
 		// in response to request specified above.
 		status 200
 	}
-}

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

7.6 Dynamic properties

The contract can contain some dynamic properties - timestamps / ids etc. You don’t want to enforce the consumers to stub their -clocks to always return the same value of time so that it gets matched by the stub. That’s why we allow you to provide the dynamic -parts in your contracts in two ways. One is to pass them directly in the -body and one to set them in a separate section called testMatchers and stubMatchers.

7.6.1 Dynamic properties inside the body

You can set the properties inside the body either via the value method

value(consumer(...), producer(...))
+}

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).

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

7.5.1 Dynamic properties inside the body

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(...))
 value(stub(...), test(...))
-value(client(...), server(...))

or if you’re using the Groovy map notation for body you can use the $() method

$(consumer(...), producer(...))
+value(client(...), server(...))

The following example shows how to set dynamic properties with $():

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

All of the aforementioned approaches are equal. That means that stub and client methods are aliases over the consumer -method. Let’s take a closer look at what we can do with those values in the subsequent sections.

7.6.2 Regular expressions

You can use regular expressions to write your requests in Contract DSL. It 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 it when you need to use patterns and not exact values both -for your test and your server side tests.

Please see the example below:

org.springframework.cloud.contract.spec.Contract.make {
+$(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.

7.5.2 Regular expressions

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 {
 	request {
 		method('GET')
 		url $(consumer(~/\/[0-9]{2}/), producer('/12'))
@@ -2305,8 +2336,9 @@ for your test and your server side tests.

Please see the example below:

'Content-Type': 'text/plain' } } -}

You can also provide only one side of the communication using a regular expression. If you do that then automatically we’ll -provide the generated string that matches the provided regular expression. For example:

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

You can also provide only one side of the communication with a regular expression. If you +do so, then the contract engine automatically provides the generated string that matches +the provided regular expression. The following code shows an example:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'PUT'
 		url value(consumer(regex('/foo/[0-9]{5}')))
@@ -2326,7 +2358,9 @@ provide the generated string that matches the provided regular expression. For e
 			contentType("application/vnd.fraud.v1+json")
 		}
 	}
-}

In this example for request and response the opposite side of the communication will have the respective data generated.

Spring Cloud Contract comes with a series of predefined regular expressions that you can use in your contracts.

protected static final Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
+}

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 ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
 protected static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?')
 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])')
@@ -2399,7 +2433,7 @@ String nonEmpty() {
 
 String nonBlank() {
 	return NON_BLANK.pattern()
-}

so in your contract you can use it like this

Contract dslWithOptionalsInString = Contract.make {
+}

In your contract, you can use it as shown in the following example:

Contract dslWithOptionalsInString = Contract.make {
 	priority 1
 	request {
 		method POST()
@@ -2422,7 +2456,8 @@ String nonBlank() {
 				message: "User not found by email = [${value(producer(regex(email())), consumer('not.existing@user.com'))}]"
 		)
 	}
-}

7.6.3 Passing optional parameters

It is possible to provide optional parameters in your contract. It’s only possible to have optional parameter for the:

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

Example:

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

7.5.3 Passing Optional Parameters

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 {
 		method 'POST'
@@ -2444,7 +2479,8 @@ String nonBlank() {
 				code: value(consumer("123123"), producer(optional("123123")))
 		)
 	}
-}

By wrapping a part of the body with the optional() method you are in fact creating a regular expression that should be present 0 or more times.

That way for the example above the following test would be generated if you pick Spock:

"""
+}

By wrapping a part of the body with the optional() method, you create a regular +expression that must be present 0 or more times.

If you use Spock for, the following test would be generated from the previous example:

"""
  given:
   def request = given()
     .header("Content-Type", "application/json")
@@ -2460,7 +2496,7 @@ String nonBlank() {
  and:
   DocumentContext parsedJson = JsonPath.parse(response.body.asString())
   assertThatJson(parsedJson).field("['code']").matches("(123123)?")
-"""

and the following stub:

'''
+"""

The following stub would also be generated:

'''
 {
   "request" : {
     "url" : "/users/password",
@@ -2485,8 +2521,9 @@ String nonBlank() {
   },
   "priority" : 1
 }
-'''

7.6.4 Executing custom methods on server side

It is also possible to define a method call to be executed on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" -in the configuration. Example:

Contract

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

7.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 +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 {
 		method 'PUT'
 		url $(consumer(regex('^/api/[0-9]{2}$')), producer('/api/12'))
@@ -2506,7 +2543,7 @@ in the configuration. Example:

Contract200 } -}

Base class

abstract class BaseMockMvcSpec extends Specification {
+}

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

abstract class BaseMockMvcSpec extends Specification {
 
 	def setup() {
 		RestAssuredMockMvc.standaloneSetup(new PairIdController())
@@ -2520,13 +2557,13 @@ in the configuration. Example:

Contract

[Important]Important

You can’t use both a String and execute to perform concatenation. E.g. calling -header('Authorization', 'Bearer ' + execute('authToken()')) will lead to improper results. -To make this work just call header('Authorization', execute('authToken()')) and ensure that -the authToken() method returns everything that you need.

The type of the object read from the JSON can be one of the followings depending on the -JSON path:

  • String if you point to a String value in a JSON
  • JSONArray if you point to a List in a JSON
  • Map if you point to a Map in a JSON
  • proper Number if you point to Integer, Double etc. in a JSON
  • Boolean if you point to a Boolean in a JSON

In the request part of the contract you can specify that the body should be -taken from a method.

[Important]Important

You have to provide both the consumer and the producer side -and the execute part can be applied for the whole body. Not for parts of it!

Example:

Contract contractDsl = Contract.make {
+}
[Important]Important

You cannot use both a String and execute to perform concatenation. For +example, calling header('Authorization', 'Bearer ' + execute('authToken()')) leads to +improper results. Instead, call header('Authorization', execute('authToken()')) and +ensure that the authToken() method returns everything you need.

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 in the JSON.
  • JSONArray: If you point to a List in the JSON.
  • Map: If you point to a Map in the JSON.
  • Number: If you point to Integer, Double etc. in the JSON.
  • Boolean: If you point to a Boolean in the JSON.

In the request part of the contract, you can specify that the body should be taken from +a method.

[Important]Important

You must provide both the consumer and the producer side. The execute part +is applied for the whole body - not for parts of it.

The following example shows how to read an object from JSON:

Contract contractDsl = Contract.make {
 	request {
 		method 'GET'
 		url '/something'
@@ -2537,8 +2574,8 @@ and the execute part can be applied for the whole b
 	response {
 		status 200
 	}
-}

This will result in calling the hashCode() method in the request body. -It would more or less like this:

// given:
+}

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

// given:
  MockMvcRequestSpecification request = given()
    .body(hashCode());
 
@@ -2547,9 +2584,12 @@ It would more or less like this:

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

7.6.5 Referencing request from response

The best situation is to provide fixed values but sometimes you need to reference a request in your response. -In order to do this you can profit from the fromRequest() method that allows you to reference a bunch -of elements from the HTTP request. You can use the following options:

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

Let’s take a look at the following contract

Contract contractDsl = Contract.make {
+ assertThat(response.statusCode()).isEqualTo(200);

7.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 +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 {
 	request {
 		method 'GET'
 		url('/api/v1/xxxx') {
@@ -2581,7 +2621,7 @@ of elements from the HTTP request. You can use the following options:

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

Running a JUnit test generation will lead in creation of a test looking more or less like this

// given:
+}

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

// given:
  MockMvcRequestSpecification request = given()
    .header("Authorization", "secret")
    .header("Authorization", "secret2")
@@ -2598,15 +2638,17 @@ of elements from the HTTP request. You can use the following options:

"Authorization")).isEqualTo("foo secret bar"); // and: DocumentContext parsedJson = JsonPath.parse(response.getBody().asString()); - assertThatJson(parsedJson).field("url").isEqualTo("/api/v1/xxxx"); - assertThatJson(parsedJson).field("fullBody").isEqualTo("{\"foo\":\"bar\",\"baz\":5}"); - assertThatJson(parsedJson).field("paramIndex").isEqualTo("bar2"); - assertThatJson(parsedJson).field("responseFoo").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization2").isEqualTo("secret2"); - assertThatJson(parsedJson).field("responseBaz").isEqualTo(5); - assertThatJson(parsedJson).field("responseBaz2").isEqualTo("Bla bla bar bla bla"); - assertThatJson(parsedJson).field("param").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization").isEqualTo("secret");

As you can see elements from the request have been properly referenced in the response.

The generated WireMock stub will look more or less like this:

{
+ assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\"foo\":\"bar\",\"baz\":5}");
+ assertThatJson(parsedJson).field("['authorization']").isEqualTo("secret");
+ assertThatJson(parsedJson).field("['authorization2']").isEqualTo("secret2");
+ assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx");
+ assertThatJson(parsedJson).field("['param']").isEqualTo("bar");
+ assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2");
+ assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1");
+ assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5);
+ assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar");
+ assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2");
+ assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla");

As you can see, elements from the request have been properly referenced in the response.

The generated WireMock stub should resemble the following example:

{
   "request" : {
     "urlPath" : "/api/v1/xxxx",
     "method" : "POST",
@@ -2621,22 +2663,24 @@ of elements from the HTTP request. You can use the following options:

} }, "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.baz == 5)]" + "matchesJsonPath" : "$[?(@.['baz'] == 5)]" }, { - "matchesJsonPath" : "$[?(@.foo == 'bar')]" + "matchesJsonPath" : "$[?(@.['foo'] == 'bar')]" } ] }, "response" : { "status" : 200, - "body" : "{\"url\":\"{{{request.url}}}\",\"param\":\"{{{request.query.foo.[0]}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\",\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\"}", + "body" : "{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}", "headers" : { - "Authorization" : "{{{request.headers.Authorization.[0]}}}" + "Authorization" : "{{{request.headers.Authorization.[0]}}};foo" }, "transformers" : [ "response-template" ] } -}

So sending a request as the one presented in the request part of the contract will lead in sending the following -response body

{
+}

Sending a request such as the one presented in the request part of the contract results +in sending the following response body:

{
   "url" : "/api/v1/xxxx?foo=bar&foo=bar2",
+  "path" : "/api/v1/xxxx",
+  "pathIndex" : "v1",
   "param" : "bar",
   "paramIndex" : "bar2",
   "authorization" : "secret",
@@ -2645,34 +2689,41 @@ response body

"responseFoo" : "bar",
   "responseBaz" : 5,
   "responseBaz2" : "Bla bla bar bla bla"
-}
[Important]Important

This feature will work only with WireMock having version greater or equal to 2.5.1. We’re using WireMock’s -response-template response transformer. It’s using Handlebars to convert the Mustache {{{ }}} templates into -proper values. Additionally we’re registering 2 helper functions. escapejsonbody - that escapes the request -body in a format that can be embedded in a JSON. Another is jsonpath that for a given parameter knows how to -find an object in the request body.

7.6.6 Dynamic properties in matchers sections

If you’ve been working with Pact this might seem familiar. Quite a few users -are used to having a separation between the body and setting dynamic parts of your contract.

That’s why you can profit from two separate sections. One is called stubMatchers where you can -define the dynamic values that should end up in a stub. You can set it in the request or inputMessage -part of your contract. The other is called testMatchers which is present in the response or -outputMessage side of the contract.

Currently we support only JSON Path based matchers with the following matching possibilities. -For stubMatchers:

  • byEquality() - the value taken from the response via the provided JSON Path needs -to be equal to the provided value in the contract
  • byRegex(…​) - the value taken from the response via the provided JSON Path needs -to match the regex
  • byDate() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Date
  • byTimestamp() - the value taken from the response via the provided JSON Path needs -to match the regex for ISO DateTime
  • byTime() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Time

For testMatchers:

  • byEquality() - the value taken from the response via the provided JSON Path needs -to be equal to the provided value in the contract
  • byRegex(…​) - the value taken from the response via the provided JSON Path needs -to match the regex
  • byDate() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Date
  • byTimestamp() - the value taken from the response via the provided JSON Path needs -to match the regex for ISO DateTime
  • byTime() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Time
  • byType() - the value taken from the 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 where you can set minOccurrence and maxOccurrence. -That way you can assert on the size of the flattened collection. To check the size -of an unflattened collection, use a custom method via byCommand(…​) testMatcher.
  • byCommand(…​) - the value taken from the response via the provided JSON Path will be -passed as an input to the custom method that you’re providing. E.g. byCommand('foo($it)') -will result in calling a foo method to which the value matching the JSON Path will get - passed.

    • The type of the object read from the JSON can be one of the followings depending on the -JSON path:

      • String if you point to a String value in a JSON
      • JSONArray if you point to a List in a JSON
      • Map if you point to a Map in a JSON
      • proper Number if you point to Integer, Double etc. in a JSON
      • Boolean if you point to a Boolean in a JSON

Let’s take a look at the following example:

Contract contractDsl = Contract.make {
+}
[Important]Important

This feature works only with WireMock having a version greater than or equal +to 2.5.1. The Spring Cloud Contract Verifier uses WireMock’s +response-template response transformer. It uses Handlebars to convert the Mustache {{{ }}} templates into +proper values. Additionally, it registers two helper functions:

  • escapejsonbody: Escapes the request body in a format that can be embedded in a JSON.
  • jsonpath: For a given parameter, find an object in the request body.

7.5.6 Registering Your Own WireMock Extension

WireMock lets you register custom extensions. By default, Spring Cloud Contract registers +the transformer, which lets you reference a request from a response. If you want to +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:

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories[indent=0]

The following is an example of a custom extension:

TestWireMockExtensions.groovy.  +

Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/TestWireMockExtensions.groovy[indent=0]

+

[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.

7.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 +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 +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 +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 {
 	request {
 		method 'GET'
 		urlPath '/get'
@@ -2778,15 +2829,20 @@ JSON path:

    valueWithoutAMatcher we’re setting - explicitly the values of regular expressions we’d like the stub to contain. For the valueWithoutAMatcher - the verification will take place in the same way as without the usage of matchers - the test - will perform an equality check in this case.

    For the response side in the testMatchers section we’re defining all the dynamic parts - in a similar manner. The only difference is that we have the byType matchers too. In that - case we’re checking 4 fields in the way that we’re verifying whether the response from the test - has a value whose JSON path matching the given field is of the same type as the one defined in the response body and:

    • for $.valueWithTypeMatch - we’re just checking the whether the type is the same
    • for $.valueWithMin - we’re checking the type and assert if the size is greater or equal to the min occurrence
    • for $.valueWithMax - we’re checking the type and assert if the size is smaller or equal to the max occurrence
    • for $.valueWithMinMax - we’re checking the type and assert if the size is between the min and max occurrence

    The resulting test would look more or less like this (note that we’re separating the autogenerated -assertions and the one from matchers with an and section):

    // given:
    +}

    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 +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 +defined in the response body, and passes the following check (based on the method being called):

    • For $.valueWithTypeMatch, the engine checks whether the type is the same.
    • For $.valueWithMin, the engine check the type and asserts whether the size is greater +than or equal to the minimum occurrence.
    • For $.valueWithMax, the engine checks the type and asserts whether the size is +smaller than or equal to the maximum occurrence.
    • For $.valueWithMinMax, the engine checks the type and asserts whether the size is +between the min and maximum occurrence.

    The resulting test would resemble the following example (note that an and section +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\"}");
    @@ -2822,10 +2878,11 @@ assertions and the one from matchers with an and se
      assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).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 we are calling the assertThatValueIsANumber. This method needs -to be defined in the test base class or should be statically imported to your tests. -Notice that the byCommand call was converted to assertThatValueIsANumber(parsedJson.read("$.duck"));. That means -that we took the method name and passed the proper JSON path as a parameter to it.

    and the WireMock stub like this:

    				'''
    + assertThatValueIsANumber(parsedJson.read("$.duck"));
    [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",
    @@ -2875,9 +2932,10 @@ that we took the method name and passed the proper JSON path as a parameter to i
         }
       }
     }
    -'''
    [Important]Important

    If you use a matcher then the part of the request / response that the matcher is addressing -via the JSON Path will get removed from assertion. In case of verifying a collection you have to create -matchers for all elements of the collection.

    Let’s look at the following example:

    Contract.make {
    +'''
    [Important]Important

    If you use a matcher, then the part of the request aned 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 {
         request {
             method 'GET'
             url("/foo")
    @@ -2901,7 +2959,7 @@ matchers for all elements of the co
                 jsonPath('$.events[0].status', byRegex('.+'))
             }
         }
    -}

    This will lead in creating the following test (showing just the assertion section)

    and:
    +}

    The preceding code leads to creating the following test (the code block shows only the assertion section):

    and:
     	DocumentContext parsedJson = JsonPath.parse(response.body.asString())
     	assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("16f1ed75-0bcc-4f0d-a04d-3121798faf99")
     	assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("EXPORT")
    @@ -2911,9 +2969,12 @@ matchers for all elements of the co
     and:
     	assertThat(parsedJson.read("\$.events[0].operation", String.class)).matches(".+")
     	assertThat(parsedJson.read("\$.events[0].eventId", String.class)).matches("^([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})\$")
    -	assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")

    As you can see the assertion is malformed. That’s because only the first element of the array got asserted. -In order to fix this it’s best to apply the assertion to the whole $.events collection and assert it -via the byCommand(…​) method.

7.7 JAX-RS support

We support JAX-RS 2 Client API. Base class needs to define protected WebTarget webTarget and server initialization, right now the only option how to test JAX-RS API is to start a web server.

Request with a body needs to have a content type set otherwise application/octet-stream is going to be used.

In order to use JAX-RS mode, use the following settings:

testMode == 'JAXRSCLIENT'

Example of a test API generated:

'''
+	assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")

As you can see, the assertion is malformed. Only the first element of the array got +asserted. In order to fix this, you should apply the assertion to the whole $.events +collection and assert it with the byCommand(…​) method.

7.6 JAX-RS Support

The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs +to define protected WebTarget webTarget and server initialization. The only option for +testing JAX-RS API is to start a web server. Also, a request with a body needs to have a +content type set. Otherwise, the default of application/octet-stream gets used.

In order to use JAX-RS mode, use the following settings:

testMode == 'JAXRSCLIENT'

The following example shows a generated test API:

'''
  // when:
   Response response = webTarget
     .path("/users")
@@ -2935,9 +2996,9 @@ via the byCommand(…​) method.

// and: DocumentContext parsedJson = JsonPath.parse(responseAsString); assertThatJson(parsedJson).field("['property1']").isEqualTo("a"); -'''

7.8 Async support

If you’re using asynchronous communication on the server side (your controllers are returning -Callable, DeferredResult etc. then inside your contract you have to provide in the response -section a async() method. Example:

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

7.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 {
     request {
         method GET()
         url '/get'
@@ -2947,8 +3008,10 @@ section a async() method. Example:

'Passed'
         async()
     }
-}

7.9 Working with Context Paths

Spring Cloud Contract supports context paths.

[Important]Important

The only thing that changes in order to fully support context paths is the switch -on the PRODUCER side. The autogenerated tests need to be using the EXPLICIT mode.

The consumer side remains untouched, in order for the generated test to pass you have to switch the EXPLICIT mode.

Maven.  +}

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

<plugin>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-contract-maven-plugin</artifactId>
@@ -2962,8 +3025,9 @@ on the PRODUCER side. The autogener
 

contracts {
 		testMode = 'EXPLICIT'
 }

-

That way you’ll generate a test that DOES NOT use MockMvc. It means that you’re generating -real requests and you need to setup your generated test’s base class to work on a real socket.

Let’s imagine the following contract:

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

That way, you generate a test that DOES NOT use MockMvc. It means that you generate +real requests and you need to setup your generated test’s base class to work on a real +socket.

Consider the following contract:

org.springframework.cloud.contract.spec.Contract.make {
 	request {
 		method 'GET'
 		url '/my-context-path/url'
@@ -2971,7 +3035,7 @@ real requests and you need to setup your generated test’s base class to wo
 	response {
 		status 200
 	}
-}

Here is an example of how to set up a base class and Rest Assured for everything to work correctly.

import com.jayway.restassured.RestAssured;
+}

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

import com.jayway.restassured.RestAssured;
 import org.junit.Before;
 import org.springframework.boot.context.embedded.LocalServerPort;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -2986,8 +3050,11 @@ real requests and you need to setup your generated test’s base class to wo
 		RestAssured.baseURI = "http://localhost";
 		RestAssured.port = this.port;
 	}
-}

That way all:

  • all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. /my-context-path/url)
  • your contracts reflect that you have a context path, thus your generated stubs will also -have that information (e.g. in the stubs you’ll see that you have too call /my-context-path/url)

7.10 Messaging Top-Level Elements

The DSL for messaging looks a little bit different than the one that focuses on HTTP.

7.10.1 Output triggered by a method

The output message can be triggered by calling a method (e.g. a Scheduler was started and a message was sent)

def dsl = Contract.make {
+}

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).

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

7.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 {
 	// Human readable description
 	description 'Some description'
 	// Label by means of which the output message can be triggered
@@ -3008,8 +3075,11 @@ have that information (e.g. in the stubs you’ll see that you have too call
 			header('BOOK-NAME', 'foo')
 		}
 	}
-}

In this case the output message will be sent to output if a method called bookReturnedTriggered will be executed. In the message publisher’s side -we will generate a test that will call that method to trigger the message. On the consumer side you can use the some_label to trigger the message.

7.10.2 Output triggered by a message

The output message can be triggered by receiving a message.

def dsl = Contract.make {
+}

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.

7.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 {
 	description 'Some Description'
 	label 'some_label'
 	// input is a message
@@ -3034,10 +3104,15 @@ we will generate a test that will call that method to trigger the message. On th
 			header('BOOK-NAME', 'foo')
 		}
 	}
-}

In this case the output message will be sent to output if a proper message will be received on the input destination. In the message publisher’s side -we will generate a test that will send the input message to the defined destination. On the consumer side you can either send a message to the input -destination or use the some_label to trigger the message.

7.10.3 Consumer / Producer

In HTTP you have a notion of client/stub and `server/test notation. You can use them also in messaging but we’re providing also the consumer and produer methods -as presented below (note you can use either $ or value methods to provide consumer and producer parts)

Contract.make {
+}

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.

7.9.3 Consumer/Producer

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 +parts):

Contract.make {
 	label 'some_label'
 	input {
 		messageFrom value(consumer('jms:output'), producer('jms:input'))
@@ -3054,7 +3129,11 @@ as presented below (note you can use either $ or 'foo'
 		])
 	}
-}

7.11 Multiple contracts in one file

It’s possible to define multiple contracts in one file. An example of such a contract can look like this

import org.springframework.cloud.contract.spec.Contract
+}

7.9.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.

7.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
 
 [
         Contract.make {
@@ -3076,8 +3155,8 @@ as presented below (note you can use either $ or 200
             }
         }
-]

In this example one contract has the name field and the other doesn’t. This will lead to generation of -two tests that will look more or less like this:

package org.springframework.cloud.contract.verifier.tests.com.hello;
+]

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;
 import com.jayway.jsonpath.DocumentContext;
@@ -3118,16 +3197,17 @@ two tests that will look more or less like this:

 			assertThat(response.statusCode()).isEqualTo(200);
 	}
 
-}

Notice that for the contract that has the name field the generated test method is named -validate_should_post_a_user. For the one that doesn’t have the name it’s called +}

Notice that, for the contract that has the name field, the generated test method is named +validate_should_post_a_user. For the one that does not have the name, it is called validate_withList_1. It corresponds to the name of the file WithList.groovy and the -index of the contract in the list.

The generated stubs will look like this

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 -contract had index 1 in the list of contracts in the file).

[Tip]Tip

As you can see it’s much better if you name your contracts since then your tests - are far more meaningful.

8. Customization

8.1 Extending the DSL

It is possible to provide your own functions to the DSL. The key requirement for this -feature was to maintain the static compatibility. Below you will be able to see an example -of:

  • creation of a JAR with reusable classes
  • referencing of these classes in the DSLs

The full example can be found here.

8.1.1 Common JAR

Below you can find three classes that we will reuse in the DSLs.

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

package com.example;
+index of the contract in the list.

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.

8. Customization

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

8.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.

8.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;
 
 import java.util.regex.Pattern;
 
@@ -3258,10 +3338,10 @@ of:

    return new ServerDslProperty( PatternUtils.ok(), "OK"); } } -//end::impl[]

8.1.2 Adding the dependency to project

In order for the plugins and IDE to be able to reference the common JAR classes you need -to pass the dependency to your project.

8.1.3 Test dependency in project’s dependencies

First add the common jar dependency as a test dependency. That way since your -contracts files are available at test resources path, automatically the -common jar classes will be visible in your Groovy files.

Maven.  +//end::impl[]

8.1.2 Adding the Dependency to the Project

In order for the plugins and IDE to be able to reference the common JAR classes, you need +to pass the dependency to your project.

8.1.3 Test the Dependency in the Project’s Dependencies

First, add the common jar dependency as a test dependency. Because your contracts files +are available on the test resources path, the common jar classes automatically become +visible in your Groovy files. The following examples show how to test the dependency:

Maven. 

<dependency>
 	<groupId>com.example</groupId>
 	<artifactId>beer-common</artifactId>
@@ -3270,7 +3350,8 @@ common jar classes will be visible in your Groovy files.

< </dependency>

Gradle. 

testCompile("com.example:beer-common:0.0.1-SNAPSHOT")

-

8.1.4 Test dependency in plugin’s dependencies

Now you have to add the dependency for the plugin to reuse at runtime.

Maven.  +

8.1.4 Test a Dependency in the Plugin’s Dependencies

Now, you must add the dependency for the plugin to reuse at runtime, as shown in the +following example:

Maven. 

<plugin>
 	<groupId>org.springframework.cloud</groupId>
 	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
@@ -3296,7 +3377,7 @@ common jar classes will be visible in your Groovy files.

< </plugin>

Gradle. 

classpath "com.example:beer-common:0.0.1-SNAPSHOT"

-

8.1.5 Referencing classes in DSLs

Now you can reference your classes in your DSL. Example:

package contracts.beer.rest
+

8.1.5 Referencing classes in DSLs

You can now reference your classes in your DSL, as shown in the following example:

package contracts.beer.rest
 
 import com.example.ConsumerUtils
 import com.example.ProducerUtils
@@ -3337,12 +3418,12 @@ then:
 			contentType(applicationJson())
 		}
 	}
-}

9. Pluggable architecture

There are cases where you have your contracts defined in other formats -like YAML, RAML or PACT. On the other hand you’d like to profit from -the test and stubs generation. It’s really easy to add your own implementation -of either of those. Also you can customize the way tests are generated (for example you can generate -tests for other languages) and you can do the same for stubs generation (you can generate -stubs for other stub http server implementations).

9.1 Custom contract converter

Let’s assume that your contract is written in a YAML file like this:

request:
+}

9. Using the Pluggable Architecture

You may encounter cases where you have your contracts have been defined in other formats, +such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic +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).

9.1 Custom Contract Converter

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

request:
   url: /foo
   method: PUT
   headers:
@@ -3354,7 +3435,8 @@ response:
   headers:
     foo2: bar
   body:
-    foo2: bar

Thanks to the interface

package org.springframework.cloud.contract.spec
+    foo2: bar

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
 
 /**
  * Converter to be used to convert FROM {@link File} TO {@link Contract}
@@ -3391,12 +3473,13 @@ response:
 	 * @return - {@link T} the type to which we do the conversion
 	 */
 	T convertTo(Collection<Contract> contract)
-}

you can register your own implementation of a contract structure converter. -Your implementation needs to state the condition on which it should start the -conversion. Also you have to define how to perform that conversion in both ways.

[Important]Important

Once you create your implementation you have to create a /META-INF/spring.factories -file in which you provide the fully qualified name of your implementation.

Example of a spring.factories file

# Converters
+}

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

and the YAML implementation

package org.springframework.cloud.contract.verifier.converter
+org.springframework.cloud.contract.verifier.converter.YamlContractConverter

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
 
@@ -3468,10 +3551,10 @@ org.springframework.cloud.contract.verifier.converter.YamlContractConverter
return yamlContract } } -}

9.1.1 Pact converter

Spring Cloud Contract comes with an out of the box support for Pact representation of contracts. -In other words instead of using the Groovy DSL you can use Pact files. In this section -we will present how to add such a support for your project.

9.1.2 Pact contract

We will be working on the following example of a Pact contract. We’ve placed this file under -the src/test/resources/contracts folder.

{
+}

9.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.

9.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"
   },
@@ -3524,9 +3607,9 @@ the src/test/resources/contracts folder.

"version": "2.4.18"
     }
   }
-}

9.1.3 Pact for producers

On the producer side you have add to your plugin configuration two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using.

Maven.  +}

The remainder of this section about using Pact refers to the preceding file.

9.1.3 Pact for Producers

On the producer side, you mustadd 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>
 	<groupId>org.springframework.cloud</groupId>
 	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
@@ -3551,7 +3634,8 @@ Pact version that you’re using.

Maven. 

Gradle. 

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

-

When you execute the build of your application a test, looking more or less like this, will be generated

@Test
+

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 {
 	// given:
 		MockMvcRequestSpecification request = given()
@@ -3570,7 +3654,7 @@ classpath 'au.co
 		assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
 	// and:
 		assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
-}

and the stub looking like this

{
+}

The corresponding generated stub might be as follows:

{
   "uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
   "request" : {
     "url" : "/fraudcheck",
@@ -3593,9 +3677,9 @@ classpath 'au.co
       "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
     }
   }
-}

9.1.4 Pact for consumers

On the producer side you have add to your project dependencies two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using.

Maven.  +}

9.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>
@@ -3610,9 +3694,9 @@ Pact version that you’re using.

Maven. 

Gradle. 

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

-

9.2 Custom test generator

If you want to generate tests for different languages than Java or you’re -not happy with the way we’re building Java tests for you then you can register -your own implementation to do that.

Thanks to the interface

package org.springframework.cloud.contract.verifier.builder
+

9.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
 
 import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
 import org.springframework.cloud.contract.verifier.file.ContractMetadata
@@ -3643,10 +3727,11 @@ your own implementation to do that.

Thanks to the interface

 	String fileExtension(ContractVerifierConfigProperties properties)
-}

you can register your own implementation that generates a test. Again, it’s enough to provide -a proper spring.factories file. Example:

org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
-com.example.MyGenerator

9.3 Custom stub generator

If you want to generate stubs for other stub server than WireMock it’s enough to - plug in your own implementation of this interface:

package org.springframework.cloud.contract.verifier.converter
+}

Again, you must provide a spring.factories file, such as the one shown in the following +example:

org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
+com.example.MyGenerator

9.3 Using the Custom Stub Generator

If you want to generate stubs for stub servers other than WireMock, you can plug in your +own implementation of the StubGenerator interface. The following code listing shows the +StubGenerator interface:

package org.springframework.cloud.contract.verifier.converter
 
 import groovy.transform.CompileStatic
 import org.springframework.cloud.contract.spec.Contract
@@ -3682,14 +3767,14 @@ com.example.MyGenerator
< * converter will create two files {@code 0_foo.json} and {@code 1_foo.json} */ String generateOutputFileNameForInput(String inputFileName) -}

you can register your own implementation that generate Stubs. Again, it’s enough to provide -a proper spring.factories file. Example:

# Stub converters
+}

Again, you must provide a spring.factories file, such as the one shown in the following +example:

# Stub converters
 org.springframework.cloud.contract.verifier.converter.StubGenerator=\
-org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

The default implementation is the WireMock stub generation.

[Tip]Tip

You can provide multiple stub generator implementations. That way for example from a single -DSL as input you can e.g. produce WireMock stubs and Pact files too!

9.4 Custom Stub Runner

If you decide to have a custom stub generation you also need a custom way of running -stubs with your different stub provider.

Let us assume that you’re using Moco to build your stubs. -You wrote a proper stub generator and your stubs got placed in a JAR file.

In order for Stub Runner to know how to run your stubs you have to define a custom - HTTP Stub server implementation. It can look like this:

package org.springframework.cloud.contract.stubrunner.provider.moco
+org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

The default implementation is the WireMock stub generation.

[Tip]Tip

You can provide multiple stub generator implementations. For example, from a single +DSL, you can produce both WireMock stubs and Pact files.

9.4 Using the Custom Stub Runner

If you decide to use a custom stub generation, you also need a custom way of running +stubs with your different stub provider.

Assume that you use Moco to build your stubs and that +you have written a stub generator and placed your stubs in a JAR file.

In order for Stub Runner to know how to run your stubs, you have to define a custom +HTTP Stub server implementation, which might resemble the following example:

package org.springframework.cloud.contract.stubrunner.provider.moco
 
 import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
 import com.github.dreamhead.moco.runner.JsonRunner
@@ -3761,10 +3846,11 @@ You wrote a proper stub generator and your stubs got placed in a JAR file.

boolean isAccepted(File file) { return file.name.endsWith(".json") } -}

and just register it in your spring.factories file

org.springframework.cloud.contract.stubrunner.HttpServerStub=\
-org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub

that way you’ll be able to run stubs using Moco.

[Important]Important

If you don’t provide any implementation then the default one - WireMock based -will be picked. If you provide more than one then the first one on the list will be picked.

9.5 Custom Stub Downloader

You can customize the way your stubs are downloaded. It’s enough to create an -implementation of the StubDownloaderBuilder

package com.example;
+}

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

org.springframework.cloud.contract.stubrunner.HttpServerStub=\
+org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub

Now you can run stubs with Moco.

[Important]Important

If you do not provide any implementation, then the default (WireMock) +implementation is used. If you provide more than one, the first one on the list is used.

9.5 Using the Custom Stub Downloader

You can customize the way your stubs are downloaded by creating an implementation of the +StubDownloaderBuilder interface, as shown in the following example:

package com.example;
 
 class CustomStubDownloaderBuilder implements StubDownloaderBuilder {
 
@@ -3783,13 +3869,14 @@ implementation of the StubDownloaderBuilder

// here goes your custom logic to provide a folder where all the stubs reside
 			}
-}

and just register it in your spring.factories file

# Example of a custom Stub Downloader Provider
+}

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

that way you’ll be able to pick a folder with the source of your stubs.

[Important]Important

If you don’t provide any implementation then the default one will be picked. - If you provide repositoryRoot property or workOffline flag then Aether based - that will download stubs from a remote repo will be picked. If you don’t provide these - values then the ClasspathStubProvider will be picked that will scan the classpath. - If you provide more than one, then the first one on the list will be picked.

10. Spring Cloud Contract WireMock

Modules giving you the possibility to use +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.

10. Spring Cloud Contract WireMock

Modules giving you the possibility to use WireMock with different servers by using the "ambient" server embedded in a Spring Boot application. Check out the samples diff --git a/1.1.x/spring-cloud-contract.xml b/1.1.x/spring-cloud-contract.xml index b230093c1d..ce9be24e0f 100644 --- a/1.1.x/spring-cloud-contract.xml +++ b/1.1.x/spring-cloud-contract.xml @@ -4,7 +4,7 @@ Spring Cloud Contract -2017-10-19 +2017-10-07 @@ -3532,19 +3532,23 @@ public void handlePerson(Person person) { Contract DSL -Remember that inside the contract file you have to provide the fully qualified name to -the Contract class and the make static import i.e. 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 { …​ } +Remember that, inside the 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 don’t be alarmed if you didn’t use Groovy before. Knowledge of the language is not really needed as our DSL uses only -a tiny subset of it (namely literals, method calls and closures). What’s more the DSL is designed to be programmer-readable without any knowledge of the DSL itself - - it’s statically typed. +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. -Spring Cloud Contract supports defining multiple contracts in a single file! +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. -Let’s look at full example of a contract definition. +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 { request { method 'PUT' @@ -3587,31 +3591,54 @@ a tiny subset of it (namely literals, method calls and closures). What’s m status 200 } } -Not all features of the DSL are used in example above. If you didn’t find what you are looking for, please check next paragraphs on this page. -

-You can easily compile Contracts to WireMock stubs mapping using standalone maven command: mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert. -
+ +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
Limitations -Spring Cloud Contract Verifier doesn’t support XML properly. Please use JSON or help us implement this feature. +Spring Cloud Contract Verifier does not properly support XML. Please use JSON or +help us implement this feature. -The support for the verification of size of JSON arrays is experimental. If you want to turn it on please provide -the value of a system property spring.cloud.contract.verifier.assert.size equal to true. By default this feature is set to -false. You can also provide the assertJsonSize property in the plugin configuration. +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. -Due to the fact that JSON structure can have any form it’s sometimes impossible to parse it properly when using -the value(consumer(…​), producer(…​)) notation when using that in GString. That’s why we highly recommend using the -Groovy Map notation. +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 +is why you should use the Groovy Map notation.
Common Top-Level elements -
+The following sections describe the most common top-level elements: + + + + + + + + + + + + + + + + + +
Description -You can add a description to your contract that is nothing else but an arbitrary text. Example: +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 { description(''' given: @@ -3623,29 +3650,54 @@ then: ''') }
-
+
Name -You can provide a name of your contract. Let’s assume that you’ve provided a name should register a user. -If you do this then the name of the autogenerated test will be equal to validate_should_register_a_user. -Also the name of the stub will be should_register_a_user.json in case of a WireMock stub. +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. -Please ensure that the name doesn’t contain any characters that will make the generated test - not possible to compile. Also remember that if you provide the same name for multiple contracts then your - autogenerated tests will fail to compile and your generated stubs will override each other. +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.
-
-Ignoring contracts -If you want to ignore a contract you can either set a value of ignored contracts in the plugin configuration -or just set the ignored property on the contract itself: +
+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 { ignored() }
+
+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: +Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/readFromFile.groovy[indent=0] +Further assume that the JSON files is as follows: +request.json +Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/request.json[indent=0] +response.json +Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-verifier/src/test/resources/classpath/response.json[indent=0] +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.
-
+
HTTP Top-Level Elements -Following methods can be called in the top-level closure of a contract definition. Request and response are mandatory, priority is optional. +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 { // Definition of HTTP request part of the contract // (this can be a valid request or invalid depending @@ -3667,9 +3719,11 @@ or just set the ignored property on the contract itself:
+
Request -HTTP protocol requires only method and address to be specified in a request. The same information is mandatory in request definition of the Contract. +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 { request { // HTTP request method (GET/POST/PUT/DELETE). @@ -3683,7 +3737,8 @@ or just set the ignored property on the contract itself: -It is possible to specify whole url instead of just path, but urlPath is the recommended way as it makes the tests host-independent. +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 { request { method 'GET' @@ -3696,7 +3751,8 @@ or just set the ignored property on the contract itself: -Request may contain query parameters, which are specified in a closure nested in a call to urlPath or url. +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 { request { //... @@ -3738,7 +3794,7 @@ or just set the ignored property on the contract itself: -It may contain additional request headers…​ +request may contain additional request headers, as shown in the following example: org.springframework.cloud.contract.spec.Contract.make { request { //... @@ -3757,7 +3813,7 @@ or just set the ignored property on the contract itself: -…​and a request body. +request may contain a request body, as shown in the following example: org.springframework.cloud.contract.spec.Contract.make { request { //... @@ -3771,7 +3827,8 @@ or just set the ignored property on the contract itself: -Request may contain multipart elements. Just call the multipart() method. +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 { request { method "PUT" @@ -3796,13 +3853,20 @@ or just set the ignored property on the contract itself: -In this example we defined parameters either directly by using the map notation, -where the value can be a dynamic property (e.g. formParameter: $(consumer(…​), producer(…​))) - or by using the named(…​) method that allows you to set a named parameter. - A named parameter can set a name and content. You can call it either via - a method with 2 arguments: e.g. named("fileName", "fileContent") or - via a map notation named(name: "fileName", content: "fileContent"). -From this contract the generated test will look more or less like this: +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 +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: MockMvcRequestSpecification request = given() .header("Content-Type", "multipart/form-data;boundary=AaB03x") @@ -3816,7 +3880,7 @@ where the value can be a dynamic property (e.g. formParameter: $(consum // then: assertThat(response.statusCode()).isEqualTo(200); -The WireMock stub will look more or less like this: +The WireMock stub is as follows: ''' { "request" : { @@ -3844,7 +3908,8 @@ where the value can be a dynamic property (e.g. formParameter: $(consum
Response -Minimal response must contain HTTP status code. +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 { request { //... @@ -3855,35 +3920,40 @@ where the value can be a dynamic property (e.g. formParameter: $(consum status 200 } } -Besides status response may contain headers and body, which are specified the same way as in the request (see previous paragraph). +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).
Dynamic properties -The contract can contain some dynamic properties - timestamps / ids etc. You don’t want to enforce the consumers to stub their -clocks to always return the same value of time so that it gets matched by the stub. That’s why we allow you to provide the dynamic -parts in your contracts in two ways. One is to pass them directly in the -body and one to set them in a separate section called testMatchers and stubMatchers. +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.
Dynamic properties inside the body -You can set the properties inside the body either via the value method +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(...)) value(stub(...), test(...)) value(client(...), server(...)) -or if you’re using the Groovy map notation for body you can use the $() method +The following example shows how to set dynamic properties with $(): $(consumer(...), producer(...)) $(c(...), p(...)) $(stub(...), test(...)) $(client(...), server(...)) -All of the aforementioned approaches are equal. That means that stub and client methods are aliases over the consumer -method. Let’s take a closer look at what we can do with those values in the subsequent sections. +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.
Regular expressions -You can use regular expressions to write your requests in Contract DSL. It 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 it when you need to use patterns and not exact values both -for your test and your server side tests. -Please see the example below: +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 { request { method('GET') @@ -3908,8 +3978,9 @@ for your test and your server side tests. } } } -You can also provide only one side of the communication using a regular expression. If you do that then automatically we’ll -provide the generated string that matches the provided regular expression. For example: +You can also provide only one side of the communication with a regular expression. If you +do so, then the contract engine automatically provides the generated string that matches +the provided regular expression. The following code shows an example: org.springframework.cloud.contract.spec.Contract.make { request { method 'PUT' @@ -3931,8 +4002,10 @@ provide the generated string that matches the provided regular expression. For e } } } -In this example for request and response the opposite side of the communication will have the respective data generated. -Spring Cloud Contract comes with a series of predefined regular expressions that you can use in your contracts. +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 ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/) protected static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?') @@ -4007,7 +4080,7 @@ String nonEmpty() { String nonBlank() { return NON_BLANK.pattern() } -so in your contract you can use it like this +In your contract, you can use it as shown in the following example: Contract dslWithOptionalsInString = Contract.make { priority 1 request { @@ -4034,8 +4107,9 @@ String nonBlank() { }
-Passing optional parameters -It is possible to provide optional parameters in your contract. It’s only possible to have optional parameter for the: +Passing Optional Parameters +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 @@ -4044,7 +4118,7 @@ String nonBlank() { TEST side of the Response -Example: +The following example shows how to provide optional parameters: org.springframework.cloud.contract.spec.Contract.make { priority 1 request { @@ -4068,8 +4142,9 @@ String nonBlank() { ) } } -By wrapping a part of the body with the optional() method you are in fact creating a regular expression that should be present 0 or more times. -That way for the example above the following test would be generated if you pick Spock: +By wrapping a part of the body with the optional() method, you create a regular +expression that must be present 0 or more times. +If you use Spock for, the following test would be generated from the previous example: """ given: def request = given() @@ -4087,7 +4162,7 @@ String nonBlank() { DocumentContext parsedJson = JsonPath.parse(response.body.asString()) assertThatJson(parsedJson).field("['code']").matches("(123123)?") """ -and the following stub: +The following stub would also be generated: ''' { "request" : { @@ -4115,11 +4190,11 @@ String nonBlank() { } '''
-
-Executing custom methods on server side -It is also possible to define a method call to be executed on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" -in the configuration. Example: -Contract +
+Executing Custom Methods on the Server Side +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 { method 'PUT' @@ -4141,7 +4216,7 @@ in the configuration. Example: status 200 } } -Base class +The following code shows the base class portion of the test case: abstract class BaseMockMvcSpec extends Specification { def setup() { @@ -4158,37 +4233,37 @@ in the configuration. Example: } -You can’t use both a String and execute to perform concatenation. E.g. calling -header('Authorization', 'Bearer ' + execute('authToken()')) will lead to improper results. -To make this work just call header('Authorization', execute('authToken()')) and ensure that -the authToken() method returns everything that you need. +You cannot use both a String and execute to perform concatenation. For +example, calling header('Authorization', 'Bearer ' + execute('authToken()')) leads to +improper results. Instead, call header('Authorization', execute('authToken()')) and +ensure that the authToken() method returns everything you need. -The type of the object read from the JSON can be one of the followings depending on the +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 in a JSON +String: If you point to a String value in the JSON. -JSONArray if you point to a List in a JSON +JSONArray: If you point to a List in the JSON. -Map if you point to a Map in a JSON +Map: If you point to a Map in the JSON. -proper Number if you point to Integer, Double etc. in a JSON +Number: If you point to Integer, Double etc. in the JSON. -Boolean if you point to a Boolean in a JSON +Boolean: If you point to a Boolean in the JSON. -In the request part of the contract you can specify that the body should be -taken from a method. +In the request part of the contract, you can specify that the body should be taken from +a method. -You have to provide both the consumer and the producer side -and the execute part can be applied for the whole body. Not for parts of it! +You must provide both the consumer and the producer side. The execute part +is applied for the whole body - not for parts of it. -Example: +The following example shows how to read an object from JSON: Contract contractDsl = Contract.make { request { method 'GET' @@ -4201,8 +4276,8 @@ and the execute part can be applied for the whole body. Not f status 200 } } -This will result in calling the hashCode() method in the request body. -It would more or less like this: +The preceding example results in calling the hashCode() method in the request body. +It should resemble the following code: // given: MockMvcRequestSpecification request = given() .body(hashCode()); @@ -4214,35 +4289,44 @@ It would more or less like this: // then: assertThat(response.statusCode()).isEqualTo(200);
-
-Referencing request from response -The best situation is to provide fixed values but sometimes you need to reference a request in your response. -In order to do this you can profit from the fromRequest() method that allows you to reference a bunch -of elements from the HTTP request. You can use the following options: +
+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 +you reference a bunch of elements from the HTTP request. You can use the following +options: -fromRequest().url() - return the request URL +fromRequest().url(): Returns the request URL and query parameters. -fromRequest().query(String key) - return the first query parameter with a given name +fromRequest().query(String key): Returns the first query parameter with a given name. -fromRequest().query(String key, int index) - return the nth query parameter with a given name +fromRequest().query(String key, int index): Returns the nth query parameter with a +given name. -fromRequest().header(String key) - return the first header with a given name +fromRequest().path(): Returns the full path. -fromRequest().header(String key, int index) - return the nth header with a given name +fromRequest().path(int index): Returns the nth path element. -fromRequest().body() - return the full request body +fromRequest().header(String key): Returns the first header with a given name. -fromRequest().body(String jsonPath) - return the element from the request that matches the JSON Path +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. -Let’s take a look at the following contract +Consider the following contract: Contract contractDsl = Contract.make { request { method 'GET' @@ -4276,7 +4360,7 @@ of elements from the HTTP request. You can use the following options: ) } } -Running a JUnit test generation will lead in creation of a test looking more or less like this +Running a JUnit test generation leads to a test that resembles the following example: // given: MockMvcRequestSpecification request = given() .header("Authorization", "secret") @@ -4294,17 +4378,19 @@ of elements from the HTTP request. You can use the following options: assertThat(response.header("Authorization")).isEqualTo("foo secret bar"); // and: DocumentContext parsedJson = JsonPath.parse(response.getBody().asString()); - assertThatJson(parsedJson).field("url").isEqualTo("/api/v1/xxxx"); - assertThatJson(parsedJson).field("fullBody").isEqualTo("{\"foo\":\"bar\",\"baz\":5}"); - assertThatJson(parsedJson).field("paramIndex").isEqualTo("bar2"); - assertThatJson(parsedJson).field("responseFoo").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization2").isEqualTo("secret2"); - assertThatJson(parsedJson).field("responseBaz").isEqualTo(5); - assertThatJson(parsedJson).field("responseBaz2").isEqualTo("Bla bla bar bla bla"); - assertThatJson(parsedJson).field("param").isEqualTo("bar"); - assertThatJson(parsedJson).field("authorization").isEqualTo("secret"); -As you can see elements from the request have been properly referenced in the response. -The generated WireMock stub will look more or less like this: + assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\"foo\":\"bar\",\"baz\":5}"); + assertThatJson(parsedJson).field("['authorization']").isEqualTo("secret"); + assertThatJson(parsedJson).field("['authorization2']").isEqualTo("secret2"); + assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx"); + assertThatJson(parsedJson).field("['param']").isEqualTo("bar"); + assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2"); + assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1"); + assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5); + assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar"); + assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2"); + assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla"); +As you can see, elements from the request have been properly referenced in the response. +The generated WireMock stub should resemble the following example: { "request" : { "urlPath" : "/api/v1/xxxx", @@ -4320,24 +4406,26 @@ of elements from the HTTP request. You can use the following options: } }, "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.baz == 5)]" + "matchesJsonPath" : "$[?(@.['baz'] == 5)]" }, { - "matchesJsonPath" : "$[?(@.foo == 'bar')]" + "matchesJsonPath" : "$[?(@.['foo'] == 'bar')]" } ] }, "response" : { "status" : 200, - "body" : "{\"url\":\"{{{request.url}}}\",\"param\":\"{{{request.query.foo.[0]}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\",\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\"}", + "body" : "{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}", "headers" : { - "Authorization" : "{{{request.headers.Authorization.[0]}}}" + "Authorization" : "{{{request.headers.Authorization.[0]}}};foo" }, "transformers" : [ "response-template" ] } } -So sending a request as the one presented in the request part of the contract will lead in sending the following -response body +Sending a request such as the one presented in the request part of the contract results +in sending the following response body: { "url" : "/api/v1/xxxx?foo=bar&foo=bar2", + "path" : "/api/v1/xxxx", + "pathIndex" : "v1", "param" : "bar", "paramIndex" : "bar2", "authorization" : "secret", @@ -4348,105 +4436,143 @@ response body "responseBaz2" : "Bla bla bar bla bla" } -This feature will work only with WireMock having version greater or equal to 2.5.1. We’re using WireMock’s -response-template response transformer. It’s using Handlebars to convert the Mustache {{{ }}} templates into -proper values. Additionally we’re registering 2 helper functions. escapejsonbody - that escapes the request -body in a format that can be embedded in a JSON. Another is jsonpath that for a given parameter knows how to -find an object in the request body. +This feature works only with WireMock having a version greater than or equal +to 2.5.1. The Spring Cloud Contract Verifier uses WireMock’s +response-template response transformer. It uses Handlebars to convert the Mustache {{{ }}} templates into +proper values. Additionally, it registers two helper functions: -
-
-Dynamic properties in matchers sections -If you’ve been working with Pact this might seem familiar. Quite a few users -are used to having a separation between the body and setting dynamic parts of your contract. -That’s why you can profit from two separate sections. One is called stubMatchers where you can -define the dynamic values that should end up in a stub. You can set it in the request or inputMessage -part of your contract. The other is called testMatchers which is present in the response or -outputMessage side of the contract. -Currently we support only JSON Path based matchers with the following matching possibilities. -For stubMatchers: -byEquality() - the value taken from the response via the provided JSON Path needs -to be equal to the provided value in the contract +escapejsonbody: Escapes the request body in a format that can be embedded in a JSON. -byRegex(…​) - the value taken from the response via the provided JSON Path needs -to match the regex - - -byDate() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Date - - -byTimestamp() - the value taken from the response via the provided JSON Path needs -to match the regex for ISO DateTime - - -byTime() - the value taken from the response via the provided JSON Path needs to -match the regex for ISO Time +jsonpath: For a given parameter, find an object in the request body. +
+
+Registering Your Own WireMock Extension +WireMock lets you register custom extensions. By default, Spring Cloud Contract registers +the transformer, which lets you reference a request from a response. If you want to +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: +Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/resources/META-INF/spring.factories[indent=0] +The following is an example of a custom extension: + +TestWireMockExtensions.groovy + +Unresolved directive in verifier_contract.adoc - include::../../../../spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/TestWireMockExtensions.groovy[indent=0] + + + +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. + +
+
+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 +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 needs -to be equal to the provided value in the contract +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 needs -to match the regex +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 needs to -match the regex for ISO Date +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 needs -to match the regex for ISO DateTime +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 needs to -match the regex for ISO Time +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 of the same type as the type defined in the body of the response in the contract. -byType can take a closure where you can set minOccurrence and maxOccurrence. -That way you can assert on the size of the flattened collection. To check the size -of an unflattened collection, use a custom method via byCommand(…​) testMatcher. +byType(): The value taken from the 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 will be -passed as an input to the custom method that you’re providing. E.g. byCommand('foo($it)') -will result in calling a foo method to which the value matching the JSON Path will get - passed. +byCommand(…​): The value taken from the 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: -The type of the object read from the JSON can be one of the followings depending on the -JSON path: - - -String if you point to a String value in a JSON +String: If you point to a String value. -JSONArray if you point to a List in a JSON +JSONArray: If you point to a List. -Map if you point to a Map in a JSON +Map: If you point to a Map. -proper Number if you point to Integer, Double etc. in a JSON +Number: If you point to Integer, Double, or other kind of number. -Boolean if you point to a Boolean in a JSON +Boolean: If you point to a Boolean. -Let’s take a look at the following example: +Consider the following example: Contract contractDsl = Contract.make { request { method 'GET' @@ -4554,31 +4680,36 @@ JSON path: } } } -In this example we’re providing the dynamic portions of the contract in the matchers sections. - For the request part you can see that for all fields but valueWithoutAMatcher we’re setting - explicitly the values of regular expressions we’d like the stub to contain. For the valueWithoutAMatcher - the verification will take place in the same way as without the usage of matchers - the test - will perform an equality check in this case. -For the response side in the testMatchers section we’re defining all the dynamic parts - in a similar manner. The only difference is that we have the byType matchers too. In that - case we’re checking 4 fields in the way that we’re verifying whether the response from the test - has a value whose JSON path matching the given field is of the same type as the one defined in the response body and: +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 +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 +defined in the response body, and passes the following check (based on the method being called): -for $.valueWithTypeMatch - we’re just checking the whether the type is the same +For $.valueWithTypeMatch, the engine checks whether the type is the same. -for $.valueWithMin - we’re checking the type and assert if the size is greater or equal to the min occurrence +For $.valueWithMin, the engine check the type and asserts whether the size is greater +than or equal to the minimum occurrence. -for $.valueWithMax - we’re checking the type and assert if the size is smaller or equal to the max occurrence +For $.valueWithMax, the engine checks the type and asserts whether the size is +smaller than or equal to the maximum occurrence. -for $.valueWithMinMax - we’re checking the type and assert if the size is between the min and max occurrence +For $.valueWithMinMax, the engine checks the type and asserts whether the size is +between the min and maximum occurrence. -The resulting test would look more or less like this (note that we’re separating the autogenerated -assertions and the one from matchers with an and section): +The resulting test would resemble the following example (note that an and section +separates the autogenerated assertions and the assertion from matchers): // given: MockMvcRequestSpecification request = given() .header("Content-Type", "application/json") @@ -4617,12 +4748,13 @@ assertions and the one from matchers with an and section): -Notice that for the byCommand method we are calling the assertThatValueIsANumber. This method needs -to be defined in the test base class or should be statically imported to your tests. -Notice that the byCommand call was converted to assertThatValueIsANumber(parsedJson.read("$.duck"));. That means -that we took the method name and passed the proper JSON path as a parameter to it. +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. -and the WireMock stub like this: +The resulting WireMock stub is in the following example: ''' { "request" : { @@ -4675,11 +4807,12 @@ that we took the method name and passed the proper JSON path as a parameter to i } ''' -If you use a matcher then the part of the request / response that the matcher is addressing -via the JSON Path will get removed from assertion. In case of verifying a collection you have to create -matchers for all elements of the collection. +If you use a matcher, then the part of the request aned 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. -Let’s look at the following example: +Consider the following example: Contract.make { request { method 'GET' @@ -4705,7 +4838,7 @@ matchers for all elements of the collection.< } } } -This will lead in creating the following test (showing just the assertion section) +The preceding code leads to creating the following test (the code block shows only the assertion section): and: DocumentContext parsedJson = JsonPath.parse(response.body.asString()) assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("16f1ed75-0bcc-4f0d-a04d-3121798faf99") @@ -4717,18 +4850,20 @@ and: assertThat(parsedJson.read("\$.events[0].operation", String.class)).matches(".+") assertThat(parsedJson.read("\$.events[0].eventId", String.class)).matches("^([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})\$") assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+") -As you can see the assertion is malformed. That’s because only the first element of the array got asserted. -In order to fix this it’s best to apply the assertion to the whole $.events collection and assert it -via the byCommand(…​) method. +As you can see, the assertion is malformed. Only the first element of the array got +asserted. In order to fix this, you should apply the assertion to the whole $.events +collection and assert it with the byCommand(…​) method.
-JAX-RS support -We support JAX-RS 2 Client API. Base class needs to define protected WebTarget webTarget and server initialization, right now the only option how to test JAX-RS API is to start a web server. -Request with a body needs to have a content type set otherwise application/octet-stream is going to be used. +JAX-RS Support +The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs +to define protected WebTarget webTarget and server initialization. The only option for +testing JAX-RS API is to start a web server. Also, a request with a body needs to have a +content type set. Otherwise, the default of application/octet-stream gets used. In order to use JAX-RS mode, use the following settings: testMode == 'JAXRSCLIENT' -Example of a test API generated: +The following example shows a generated test API: ''' // when: Response response = webTarget @@ -4754,10 +4889,10 @@ via the byCommand(…​) method. '''
-Async support -If you’re using asynchronous communication on the server side (your controllers are returning -Callable, DeferredResult etc. then inside your contract you have to provide in the response -section a async() method. Example: +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 { request { method GET() @@ -4774,10 +4909,11 @@ section a async() method. Example: Working with Context Paths Spring Cloud Contract supports context paths. -The only thing that changes in order to fully support context paths is the switch -on the PRODUCER side. The autogenerated tests need to be using the EXPLICIT mode. +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. -The consumer side remains untouched, in order for the generated test to pass you have to switch the EXPLICIT mode. Maven @@ -4800,9 +4936,10 @@ on the PRODUCER side. The autogenerated tests } -That way you’ll generate a test that DOES NOT use MockMvc. It means that you’re generating -real requests and you need to setup your generated test’s base class to work on a real socket. -Let’s imagine the following contract: +That way, you generate a test that DOES NOT use MockMvc. It means that you generate +real requests and you need to setup your generated test’s base class to work on a real +socket. +Consider the following contract: org.springframework.cloud.contract.spec.Contract.make { request { method 'GET' @@ -4812,7 +4949,7 @@ real requests and you need to setup your generated test’s base class to wo status 200 } } -Here is an example of how to set up a base class and Rest Assured for everything to work correctly. +The following example shows how to set up a base class and Rest Assured: import com.jayway.restassured.RestAssured; import org.junit.Before; import org.springframework.boot.context.embedded.LocalServerPort; @@ -4829,23 +4966,40 @@ class ContextPathTestingBaseClass { RestAssured.port = this.port; } } -That way all: +If you do it this way: -all your requests in the autogenerated tests will be sent to the real endpoint with your context path included (e.g. /my-context-path/url) +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, thus your generated stubs will also -have that information (e.g. in the stubs you’ll see that you have too call /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).
Messaging Top-Level Elements -The DSL for messaging looks a little bit different than the one that focuses on HTTP. -
-Output triggered by a method -The output message can be triggered by calling a method (e.g. a Scheduler was started and a message was sent) +The DSL for messaging looks a little bit different than the one that focuses on HTTP. The +following sections explain the differences: + + + + + + + + + + + + + + +
+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 { // Human readable description description 'Some description' @@ -4868,12 +5022,15 @@ have that information (e.g. in the stubs you’ll see that you have too call } } } -In this case the output message will be sent to output if a method called bookReturnedTriggered will be executed. In the message publisher’s side -we will generate a test that will call that method to trigger the message. On the consumer side you can use the some_label to trigger the message. +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.
-
-Output triggered by a message -The output message can be triggered by receiving a message. +
+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 { description 'Some Description' label 'some_label' @@ -4900,14 +5057,19 @@ we will generate a test that will call that method to trigger the message. On th } } } -In this case the output message will be sent to output if a proper message will be received on the input destination. In the message publisher’s side -we will generate a test that will send the input message to the defined destination. On the consumer side you can either send a message to the input -destination or use the some_label to trigger the message. +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.
-
-Consumer / Producer -In HTTP you have a notion of client/stub and `server/test notation. You can use them also in messaging but we’re providing also the consumer and produer methods -as presented below (note you can use either $ or value methods to provide consumer and producer parts) +
+Consumer/Producer +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 +parts): Contract.make { label 'some_label' input { @@ -4927,10 +5089,18 @@ as presented below (note you can use either $ or val } }
+
+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. +
-Multiple contracts in one file -It’s possible to define multiple contracts in one file. An example of such a contract can look like this +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 [ @@ -4954,8 +5124,8 @@ as presented below (note you can use either $ or val } } ] -In this example one contract has the name field and the other doesn’t. This will lead to generation of -two tests that will look more or less like this: +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; @@ -4998,41 +5168,43 @@ public class V1Test extends TestBase { } } -Notice that for the contract that has the name field the generated test method is named -validate_should_post_a_user. For the one that doesn’t have the name it’s called +Notice that, for the contract that has the name field, the generated test method is named +validate_should_post_a_user. For the one that does not have the name, it is called validate_withList_1. It corresponds to the name of the file WithList.groovy and the index of the contract in the list. -The generated stubs will look 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 -contract had index 1 in the list of contracts in the file). +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). -As you can see it’s much better if you name your contracts since then your tests - are far more meaningful. +As you can see, it iss much better if you name your contracts because doing so makes +your tests far more meaningful.
Customization +You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in +the remainder of this section.
Extending the DSL -It is possible to provide your own functions to the DSL. The key requirement for this -feature was to maintain the static compatibility. Below you will be able to see an example -of: +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: -creation of a JAR with reusable classes +Creating a JAR with reusable classes. -referencing of these classes in the DSLs +Referencing of these classes in the DSLs. -The full example can be found here. +You can find the full example +here.
Common JAR -Below you can find three classes that we will reuse in the DSLs. +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; @@ -5171,16 +5343,16 @@ public class ProducerUtils { } //end::impl[]
-
-Adding the dependency to project -In order for the plugins and IDE to be able to reference the common JAR classes you need +
+Adding the Dependency to the Project +In order for the plugins and IDE to be able to reference the common JAR classes, you need to pass the dependency to your project.
-
-Test dependency in project’s dependencies -First add the common jar dependency as a test dependency. That way since your -contracts files are available at test resources path, automatically the -common jar classes will be visible in your Groovy files. +
+Test the Dependency in the Project’s Dependencies +First, add the common jar dependency as a test dependency. Because your contracts files +are available on the test resources path, the common jar classes automatically become +visible in your Groovy files. The following examples show how to test the dependency: Maven @@ -5199,9 +5371,10 @@ common jar classes will be visible in your Groovy files.
-
-Test dependency in plugin’s dependencies -Now you have to add the dependency for the plugin to reuse at runtime. +
+Test a Dependency in the Plugin’s Dependencies +Now, you must add the dependency for the plugin to reuse at runtime, as shown in the +following example: Maven @@ -5239,7 +5412,7 @@ common jar classes will be visible in your Groovy files.
Referencing classes in DSLs -Now you can reference your classes in your DSL. Example: +You can now reference your classes in your DSL, as shown in the following example: package contracts.beer.rest import com.example.ConsumerUtils @@ -5285,17 +5458,17 @@ then:
- -Pluggable architecture -There are cases where you have your contracts defined in other formats -like YAML, RAML or PACT. On the other hand you’d like to profit from -the test and stubs generation. It’s really easy to add your own implementation -of either of those. Also you can customize the way tests are generated (for example you can generate -tests for other languages) and you can do the same for stubs generation (you can generate -stubs for other stub http server implementations). + +Using the Pluggable Architecture +You may encounter cases where you have your contracts have been defined in other formats, +such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic +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).
-Custom contract converter -Let’s assume that your contract is written in a YAML file like this: +Custom Contract Converter +Assume that your contract is written in a YAML file as follows: request: url: /foo method: PUT @@ -5309,7 +5482,8 @@ response: foo2: bar body: foo2: bar -Thanks to the interface +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 /** @@ -5348,18 +5522,19 @@ interface ContractConverter<T> { */ T convertTo(Collection<Contract> contract) } -you can register your own implementation of a contract structure converter. -Your implementation needs to state the condition on which it should start the -conversion. Also you have to define how to perform that conversion in both ways. +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. -Once you create your implementation you have to create a /META-INF/spring.factories -file in which you provide the fully qualified name of your implementation. +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. -Example of a spring.factories file +The following example shows a typical spring.factories file: # Converters org.springframework.cloud.contract.spec.ContractConverter=\ org.springframework.cloud.contract.verifier.converter.YamlContractConverter -and the YAML implementation +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 @@ -5434,15 +5609,15 @@ class YamlContractConverter implements ContractConverter<List<YamlContract } }
-Pact converter -Spring Cloud Contract comes with an out of the box support for Pact representation of contracts. -In other words instead of using the Groovy DSL you can use Pact files. In this section -we will present how to add such a support for your project. +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.
-Pact contract -We will be working on the following example of a Pact contract. We’ve placed this file under -the src/test/resources/contracts folder. +Pact Contract +Consider following example of a Pact contract, which is a file under the +src/test/resources/contracts folder. { "provider": { "name": "Provider" @@ -5497,12 +5672,13 @@ the src/test/resources/contracts folder. } } } +The remainder of this section about using Pact refers to the preceding file.
-Pact for producers -On the producer side you have add to your plugin configuration two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using. +Pact for Producers +On the producer side, you mustadd 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 @@ -5536,7 +5712,8 @@ Pact version that you’re using. classpath 'au.com.dius:pact-jvm-model:2.4.18' -When you execute the build of your application a test, looking more or less like this, will be generated +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 { // given: @@ -5557,7 +5734,7 @@ public void validate_shouldMarkClientAsFraud() throws Exception { // and: assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD"); } -and the stub looking like this +The corresponding generated stub might be as follows: { "uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62", "request" : { @@ -5584,10 +5761,10 @@ public void validate_shouldMarkClientAsFraud() throws Exception { }
-Pact for consumers -On the producer side you have add to your project dependencies two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using. +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 @@ -5613,12 +5790,12 @@ testCompile 'au.com.dius:pact-jvm-model:2.4.18'
-
-Custom test generator -If you want to generate tests for different languages than Java or you’re -not happy with the way we’re building Java tests for you then you can register -your own implementation to do that. -Thanks to the interface +
+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 import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties @@ -5651,15 +5828,16 @@ interface SingleTestGenerator { */ String fileExtension(ContractVerifierConfigProperties properties) } -you can register your own implementation that generates a test. Again, it’s enough to provide -a proper spring.factories file. Example: +Again, you must provide a spring.factories file, such as the one shown in the following +example: org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/ com.example.MyGenerator
-
-Custom stub generator -If you want to generate stubs for other stub server than WireMock it’s enough to - plug in your own implementation of this interface: +
+Using the Custom Stub Generator +If you want to generate stubs for stub servers other than WireMock, you can plug in your +own implementation of the StubGenerator interface. The following code listing shows the +StubGenerator interface: package org.springframework.cloud.contract.verifier.converter import groovy.transform.CompileStatic @@ -5697,25 +5875,25 @@ interface StubGenerator { */ String generateOutputFileNameForInput(String inputFileName) } -you can register your own implementation that generate Stubs. Again, it’s enough to provide -a proper spring.factories file. Example: +Again, you must provide a spring.factories file, such as the one shown in the following +example: # Stub converters org.springframework.cloud.contract.verifier.converter.StubGenerator=\ org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter The default implementation is the WireMock stub generation. -You can provide multiple stub generator implementations. That way for example from a single -DSL as input you can e.g. produce WireMock stubs and Pact files too! +You can provide multiple stub generator implementations. For example, from a single +DSL, you can produce both WireMock stubs and Pact files.
-
-Custom Stub Runner -If you decide to have a custom stub generation you also need a custom way of running +
+Using the Custom Stub Runner +If you decide to use a custom stub generation, you also need a custom way of running stubs with your different stub provider. -Let us assume that you’re using Moco to build your stubs. -You wrote a proper stub generator and your stubs got placed in a JAR file. -In order for Stub Runner to know how to run your stubs you have to define a custom - HTTP Stub server implementation. It can look like this: +Assume that you use Moco to build your stubs and that +you have written a stub generator and placed your stubs in a JAR file. +In order for Stub Runner to know how to run your stubs, you have to define a custom +HTTP Stub server implementation, which might resemble the following example: package org.springframework.cloud.contract.stubrunner.provider.moco import com.github.dreamhead.moco.bootstrap.arg.HttpArgs @@ -5789,19 +5967,20 @@ class MocoHttpServerStub implements HttpServerStub { return file.name.endsWith(".json") } } -and just register it in your spring.factories file +Then, you can register it in your spring.factories file, as shown in the following +example: org.springframework.cloud.contract.stubrunner.HttpServerStub=\ org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub -that way you’ll be able to run stubs using Moco. +Now you can run stubs with Moco. -If you don’t provide any implementation then the default one - WireMock based -will be picked. If you provide more than one then the first one on the list will be picked. +If you do not provide any implementation, then the default (WireMock) +implementation is used. If you provide more than one, the first one on the list is used.
-
-Custom Stub Downloader -You can customize the way your stubs are downloaded. It’s enough to create an -implementation of the StubDownloaderBuilder +
+Using the Custom Stub Downloader +You can customize the way your stubs are downloaded by creating an implementation of the +StubDownloaderBuilder interface, as shown in the following example: package com.example; class CustomStubDownloaderBuilder implements StubDownloaderBuilder { @@ -5822,17 +6001,18 @@ class CustomStubDownloaderBuilder implements StubDownloaderBuilder { // here goes your custom logic to provide a folder where all the stubs reside } } -and just register it in your spring.factories file +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 -that way you’ll be able to pick a folder with the source of your stubs. +Now you can pick a folder with the source of your stubs. -If you don’t provide any implementation then the default one will be picked. - If you provide repositoryRoot property or workOffline flag then Aether based - that will download stubs from a remote repo will be picked. If you don’t provide these - values then the ClasspathStubProvider will be picked that will scan the classpath. - If you provide more than one, then the first one on the list will be picked. +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.