diff --git a/multi/multi__customization.html b/multi/multi__customization.html index 80d3ac0313..885bc14127 100644 --- a/multi/multi__customization.html +++ b/multi/multi__customization.html @@ -145,7 +145,7 @@ visible in your Groovy files. The following examples show how to test the depend <scope>test</scope> </dependency>

Gradle.  -

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

+

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

9.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>
@@ -172,7 +172,7 @@ following example:

Maven.  </dependencies> </plugin>

Gradle.  -

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

+

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

9.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
@@ -214,4 +214,4 @@ then:
 			contentType(applicationJson())
 		}
 	}
-}
\ No newline at end of file +}
[Important]Important

You can set the Spring Cloud Contract plugin up by setting convertToYaml to true. That way you will NOT have to add the dependency with the extended functionality to the consumer side, since the consumer side will be using YAML contracts instead of Groovy ones.

\ No newline at end of file diff --git a/multi/multi__spring_cloud_contract_faq.html b/multi/multi__spring_cloud_contract_faq.html index 96231dcb45..be2399546f 100644 --- a/multi/multi__spring_cloud_contract_faq.html +++ b/multi/multi__spring_cloud_contract_faq.html @@ -457,10 +457,95 @@ the `publish` task is executed publish.dependsOn("publishStubsToScm")

With such a setup:

Keeping contracts with the producer and stubs in an external repository

It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo. +META-INF/com.example/foo/1.0.0/contracts

  • Tests will be generated from the contracts
  • Stubs will be created from the contracts
  • Once the tests pass, the stubs will be committed in the cloned repository
  • Finally, a push will be done to that repo’s origin
  • 3.6.3 Producer with contracts stored locally

    Another option to use the SCM as the destination for stubs and contracts is to store the contracts locally, with the producer, and only push the contracts and the stubs to SCM. Below, you can find the setup required to achieve this using Maven and Gradle.

    Maven.  +

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +	<!-- In the default configuration, we want to use the contracts stored locally -->
    +	<configuration>
    +		<baseClassMappings>
    +			<baseClassMapping>
    +				<contractPackageRegex>.*messaging.*</contractPackageRegex>
    +				<baseClassFQN>com.example.BeerMessagingBase</baseClassFQN>
    +			</baseClassMapping>
    +			<baseClassMapping>
    +				<contractPackageRegex>.*rest.*</contractPackageRegex>
    +				<baseClassFQN>com.example.BeerRestBase</baseClassFQN>
    +			</baseClassMapping>
    +		</baseClassMappings>
    +		<basePackageForTests>com.example</basePackageForTests>
    +	</configuration>
    +	<executions>
    +		<execution>
    +			<phase>package</phase>
    +			<goals>
    +				<!-- By default we will not push the stubs back to SCM,
    +				you have to explicitly add it as a goal -->
    +				<goal>pushStubsToScm</goal>
    +			</goals>
    +			<configuration>
    +				<!-- We want to pick contracts from a Git repository -->
    +				<contractsRepositoryUrl>git://file://${env.ROOT}/target/contract_empty_git/</contractsRepositoryUrl>
    +				<!-- Example of URL via git protocol -->
    +				<!--<contractsRepositoryUrl>git://git@github.com:spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>-->
    +				<!-- Example of URL via http protocol -->
    +				<!--<contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>-->
    +				<!-- We reuse the contract dependency section to set up the path
    +				to the folder that contains the contract definitions. In our case the
    +				path will be /groupId/artifactId/version/contracts -->
    +				<contractDependency>
    +					<groupId>${project.groupId}</groupId>
    +					<artifactId>${project.artifactId}</artifactId>
    +					<version>${project.version}</version>
    +				</contractDependency>
    +				<!-- The mode can't be classpath -->
    +				<contractsMode>LOCAL</contractsMode>
    +			</configuration>
    +		</execution>
    +	</executions>
    +</plugin>

    +

    Gradle.  +

    contracts {
    +		// Base package for generated tests
    +	basePackageForTests = "com.example"
    +	baseClassMappings {
    +		baseClassMapping(".*messaging.*", "com.example.BeerMessagingBase")
    +		baseClassMapping(".*rest.*", "com.example.BeerRestBase")
    +	}
    +}
    +
    +/*
    +In this scenario we want to publish stubs to SCM whenever
    +the `publish` task is executed
    +*/
    +publishStubsToScm {
    +	// We want to modify the default set up of the plugin when publish stubs to scm is called
    +	customize {
    +		// We want to pick contracts from a Git repository
    +		contractDependency {
    +			stringNotation = "${project.group}:${project.name}:${project.version}"
    +		}
    +		/*
    +		We reuse the contract dependency section to set up the path
    +		to the folder that contains the contract definitions. In our case the
    +		path will be /groupId/artifactId/version/contracts
    +		 */
    +		contractRepository {
    +			repositoryUrl = "git://file://${System.getenv("ROOT")}/target/contract_empty_git/"
    +		}
    +		// The mode can't be classpath
    +		contractsMode = "LOCAL"
    +	}
    +}
    +
    +publish.dependsOn("publishStubsToScm")
    +publishToMavenLocal.dependsOn("publishStubsToScm")

    +

    With such a setup:

    Keeping contracts with the producer and stubs in an external repository

    It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo. This is most useful when you want to use the base consumer-producer collaboration flow, but do not have a possibility to use an artifact repository for storing the stubs.

    In order to do that, use the usual producer setup, and then add the pushStubsToScm goal and set -contractsRepositoryUrl to the repository where you want to keep the stubs.

    3.6.3 Consumer

    On the consumer side when passing the repositoryRoot parameter, +contractsRepositoryUrl to the repository where you want to keep the stubs.

    3.6.4 Consumer

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

    @AutoConfigureStubRunner(
    diff --git a/multi/multi__spring_cloud_contract_verifier_introduction.html b/multi/multi__spring_cloud_contract_verifier_introduction.html
    index 31ded5b55b..5b366a5a1b 100644
    --- a/multi/multi__spring_cloud_contract_verifier_introduction.html
    +++ b/multi/multi__spring_cloud_contract_verifier_introduction.html
    @@ -638,6 +638,7 @@ First, add the Spring Cloud Contract BOM.

    <extensions>true</extensions>
     	<configuration>
     		<packageWithBaseClasses>com.example.fraud</packageWithBaseClasses>
    +		<convertToYaml>true</convertToYaml>
     	</configuration>
     </plugin>

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

    • generate and run tests
    • produce and install stubs

    You do not want to generate tests since you, as the consumer, want only to play with the @@ -703,6 +704,7 @@ $ git pull https://your-git-server.com/server-side-fork.git contract-change-pr<extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> + <convertToYaml>true</convertToYaml> </configuration> </plugin>

    [Important]Important

    This example uses "convention based" naming by setting the packageWithBaseClasses property. Doing so means that the two last packages combine to diff --git a/multi/multi__spring_cloud_contract_verifier_setup.html b/multi/multi__spring_cloud_contract_verifier_setup.html index 08185f0e04..3fa864e5c5 100644 --- a/multi/multi__spring_cloud_contract_verifier_setup.html +++ b/multi/multi__spring_cloud_contract_verifier_setup.html @@ -144,7 +144,7 @@ closure to set it up.

  • cont downloaded, the path defaults to groupid/artifactid where groupid is slash separated. Otherwise, it scans contracts under the provided directory.
  • contractsMode: Specifies the mode of downloading contracts (whether the JAR is available offline, remotely etc.)
  • deleteStubsAfterTest: If set to false will not remove any downloaded -contracts from temporary directories
  • 4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base +contracts from temporary directories

    Below you can find a list of experimental features you can turn on via the plugin:

    • convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side.
    • assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default.

    4.2.8 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base ++ or 1.0.0.BUILD-SNAPSHOT won’t get cached). By default, this feature is turned on.

    Below you can find a list of experimental features you can turn on via the plugin:

    • convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side.
    • assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default.

    4.2.8 Single Base Class for All Tests

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

    package org.mycompany.tests
     
    diff --git a/multi/multi__using_the_pluggable_architecture.html b/multi/multi__using_the_pluggable_architecture.html
    index d40e580021..fef39d038d 100644
    --- a/multi/multi__using_the_pluggable_architecture.html
    +++ b/multi/multi__using_the_pluggable_architecture.html
    @@ -438,10 +438,10 @@ to clone the repository and use it as a source of contracts
     to generate tests or stubs.

    Either via environment variables, system properties, properties set inside the plugin or contracts repository configuration you can tweak the downloader’s behaviour. Below you can find the list of -properties

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop)

    * stubrunner.properties.git.branch (system prop)

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop)

    * stubrunner.properties.git.username (system prop)

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop)

    * stubrunner.properties.git.password (system prop)

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop)

    * stubrunner.properties.git.no-of-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop)

    * stubrunner.properties.git.wait-between-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol +properties

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop)

    * stubrunner.properties.git.branch (system prop)

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop)

    * stubrunner.properties.git.username (system prop)

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop)

    * stubrunner.properties.git.password (system prop)

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop)

    * stubrunner.properties.git.no-of-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop)

    * stubrunner.properties.git.wait-between-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol (starts with pact://), the stub downloader will try to fetch the Pact contract definitions from the Pact Broker. Whatever is set after pact:// will be parsed as the Pact Broker URL.

    Either via environment variables, system properties, properties set inside the plugin or contracts repository configuration you can tweak the downloader’s behaviour. Below you can find the list of -properties

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop)

    * stubrunner.properties.pactbroker.host (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop)

    * stubrunner.properties.pactbroker.port (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop)

    * stubrunner.properties.pactbroker.protocol (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop)

    * stubrunner.properties.pactbroker.tags (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop)

    * stubrunner.properties.pactbroker.auth.scheme (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop)

    * stubrunner.properties.pactbroker.auth.username (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop)

    * stubrunner.properties.pactbroker.auth.password (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop)

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    \ No newline at end of file +properties

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop)

    * stubrunner.properties.pactbroker.host (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop)

    * stubrunner.properties.pactbroker.port (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop)

    * stubrunner.properties.pactbroker.protocol (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop)

    * stubrunner.properties.pactbroker.tags (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop)

    * stubrunner.properties.pactbroker.auth.scheme (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop)

    * stubrunner.properties.pactbroker.auth.username (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop)

    * stubrunner.properties.pactbroker.auth.password (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop)

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    \ No newline at end of file diff --git a/multi/multi_contract-dsl.html b/multi/multi_contract-dsl.html index ff57b2c73f..cdea883017 100644 --- a/multi/multi_contract-dsl.html +++ b/multi/multi_contract-dsl.html @@ -656,80 +656,80 @@ use in your contracts, as shown in the following example:

    return Pattern.compile(values.collect({"^$it\$"}).join("|"))
     }
     
    -Pattern onlyAlphaUnicode() {
    -	return ONLY_ALPHA_UNICODE
    +RegexProperty onlyAlphaUnicode() {
    +	return new RegexProperty(ONLY_ALPHA_UNICODE).asString()
     }
     
    -Pattern alphaNumeric() {
    -	return ALPHA_NUMERIC
    +RegexProperty alphaNumeric() {
    +	return new RegexProperty(ALPHA_NUMERIC).asString()
     }
     
    -Pattern number() {
    -	return NUMBER
    +RegexProperty number() {
    +	return new RegexProperty(NUMBER).asDouble()
     }
     
    -Pattern positiveInt() {
    -	return POSITIVE_INT
    +RegexProperty positiveInt() {
    +	return new RegexProperty(POSITIVE_INT).asInteger()
     }
     
    -Pattern anyBoolean() {
    -	return TRUE_OR_FALSE
    +RegexProperty anyBoolean() {
    +	return new RegexProperty(TRUE_OR_FALSE).asBooleanType()
     }
     
    -Pattern anInteger() {
    -	return INTEGER
    +RegexProperty anInteger() {
    +	return new RegexProperty(INTEGER).asInteger()
     }
     
    -Pattern aDouble() {
    -	return DOUBLE
    +RegexProperty aDouble() {
    +	return new RegexProperty(DOUBLE).asDouble()
     }
     
    -Pattern ipAddress() {
    -	return IP_ADDRESS
    +RegexProperty ipAddress() {
    +	return new RegexProperty(IP_ADDRESS).asString()
     }
     
    -Pattern hostname() {
    -	return HOSTNAME_PATTERN
    +RegexProperty hostname() {
    +	return new RegexProperty(HOSTNAME_PATTERN).asString()
     }
     
    -Pattern email() {
    -	return EMAIL
    +RegexProperty email() {
    +	return new RegexProperty(EMAIL).asString()
     }
     
    -Pattern url() {
    -	return URL
    +RegexProperty url() {
    +	return new RegexProperty(URL).asString()
     }
     
    -Pattern httpsUrl() {
    -	return HTTPS_URL
    +RegexProperty httpsUrl() {
    +	return new RegexProperty(HTTPS_URL).asString()
     }
     
    -Pattern uuid(){
    -	return UUID
    +RegexProperty uuid(){
    +	return new RegexProperty(UUID).asString()
     }
     
    -Pattern isoDate() {
    -	return ANY_DATE
    +RegexProperty isoDate() {
    +	return new RegexProperty(ANY_DATE).asString()
     }
     
    -Pattern isoDateTime() {
    -	return ANY_DATE_TIME
    +RegexProperty isoDateTime() {
    +	return new RegexProperty(ANY_DATE_TIME).asString()
     }
     
    -Pattern isoTime() {
    -	return ANY_TIME
    +RegexProperty isoTime() {
    +	return new RegexProperty(ANY_TIME).asString()
     }
     
    -Pattern iso8601WithOffset() {
    -	return ISO8601_WITH_OFFSET
    +RegexProperty iso8601WithOffset() {
    +	return new RegexProperty(ISO8601_WITH_OFFSET).asString()
     }
     
    -Pattern nonEmpty() {
    -	return NON_EMPTY
    +RegexProperty nonEmpty() {
    +	return new RegexProperty(NON_EMPTY).asString()
     }
     
    -Pattern nonBlank() {
    -	return NON_BLANK
    +RegexProperty nonBlank() {
    +	return new RegexProperty(NON_BLANK).asString()
     }

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

    Contract dslWithOptionalsInString = Contract.make {
         priority 1
         request {
    @@ -1109,7 +1109,7 @@ This section is present in the response or 

    Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the following matching possibilities:

    Groovy DSL

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

      • byEquality(): The value taken from the consumer’s request via the provided JSON Path must be equal to the value provided in the contract.
      • byRegex(…​): The value taken from the consumer’s request via the provided JSON Path must -match the regex.
      • byDate(): The value taken from the consumer’s request via the provided JSON Path must +match the regex. You can also pass the type of the expected matched value (e.g. asString(), asLong() etc.)
      • byDate(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO Date value.
      • byTimestamp(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO DateTime value.
      • byTime(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO Time value.
    • For the verification(in generated tests on the Producer’s side):

      • byEquality(): The value taken from the producer’s response via the provided JSON Path must be @@ -1119,7 +1119,7 @@ the regex for an ISO Date value.
      • match the regex for an ISO DateTime value.
      • byTime(): The value taken from the producer’s response via the provided JSON Path must match the regex for an ISO Time value.
      • byType(): The value taken from the producer’s response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. -byType can take a closure, in which you can set minOccurrence and maxOccurrence. +byType can take a closure, in which you can set minOccurrence and maxOccurrence. For the request side, you should use the closure to assert size of the collection. 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 producer’s response via the provided JSON Path is passed as an input to the custom method that you provide. For example, @@ -1128,11 +1128,12 @@ JSON Path gets passed. The type of the object read from the JSON can be one of t following, depending on the JSON path:

        • String: If you point to a String value.
        • JSONArray: If you point to a List.
        • Map: If you point to a Map.
        • Number: If you point to Integer, Double, or other kind of number.
        • Boolean: If you point to a Boolean.
      • byNull(): The value taken from the response via the provided JSON Path must be null

    YAML. Please read the Groovy section for detailed explanation of what the types mean

    For YAML the structure of a matcher looks like this

    - path: $.foo
       type: by_regex
    -  value: bar

    Or if you want to use one of the predefined regular expressions + value: bar + regexType: as_string

    Or if you want to use one of the predefined regular expressions [only_alpha_unicode, number, any_boolean, ip_address, hostname, email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]:

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

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

    • For stubMatchers:

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

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

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

    Consider the following example:

    Groovy DSL.  + predefined: only_alpha_unicode

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

    • For stubMatchers:

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

        • there are 2 additional fields accepted: minOccurrence and maxOccurrence.
    • For testMatchers:

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

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

    You can also define which type the regular expression corresponds to via the regexType field. Below you can find the allowed list of regular expression types:

    • as_integer
    • as_double
    • as_float,
    • as_long
    • as_short
    • as_boolean
    • as_string

    Consider the following example:

    Groovy DSL. 

    Contract contractDsl = Contract.make {
     	request {
     		method 'GET'
    @@ -1152,12 +1153,12 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
     				]
     		])
     		bodyMatchers {
    -			jsonPath('$.duck', byRegex("[0-9]{3}"))
    +			jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
     			jsonPath('$.duck', byEquality())
    -			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
    +			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
     			jsonPath('$.alpha', byEquality())
    -			jsonPath('$.number', byRegex(number()))
    -			jsonPath('$.aBoolean', byRegex(anyBoolean()))
    +			jsonPath('$.number', byRegex(number()).asInteger())
    +			jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
     			jsonPath('$.date', byDate())
     			jsonPath('$.dateTime', byTimestamp())
     			jsonPath('$.time', byTime())
    @@ -1201,18 +1202,18 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
     		])
     		bodyMatchers {
     			// asserts the jsonpath value against manual regex
    -			jsonPath('$.duck', byRegex("[0-9]{3}"))
    +			jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
     			// asserts the jsonpath value against the provided value
     			jsonPath('$.duck', byEquality())
     			// asserts the jsonpath value against some default regex
    -			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
    +			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
     			jsonPath('$.alpha', byEquality())
    -			jsonPath('$.number', byRegex(number()))
    -			jsonPath('$.positiveInteger', byRegex(anInteger()))
    -			jsonPath('$.negativeInteger', byRegex(anInteger()))
    -			jsonPath('$.positiveDecimalNumber', byRegex(aDouble()))
    -			jsonPath('$.negativeDecimalNumber', byRegex(aDouble()))
    -			jsonPath('$.aBoolean', byRegex(anyBoolean()))
    +			jsonPath('$.number', byRegex(number()).asInteger())
    +			jsonPath('$.positiveInteger', byRegex(anInteger()).asInteger())
    +			jsonPath('$.negativeInteger', byRegex(anInteger()).asInteger())
    +			jsonPath('$.positiveDecimalNumber', byRegex(aDouble()).asDouble())
    +			jsonPath('$.negativeDecimalNumber', byRegex(aDouble()).asDouble())
    +			jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
     			// asserts vs inbuilt time related regex
     			jsonPath('$.date', byDate())
     			jsonPath('$.dateTime', byTimestamp())
    @@ -1282,6 +1283,20 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
         key:
           "complex.key": 'foo'
         nullValue: null
    +    valueWithMin:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinEmpty: []
    +    valueWithMaxEmpty: []
       matchers:
         url:
           regex: /get/[0-9]
    @@ -1344,6 +1359,16 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
             type: by_equality
           - path: $.nullvalue
             type: by_null
    +      - path: $.valueWithMin
    +        type: by_type
    +        minOccurrence: 1
    +      - path: $.valueWithMax
    +        type: by_type
    +        maxOccurrence: 3
    +      - path: $.valueWithMinMax
    +        type: by_type
    +        minOccurrence: 1
    +        maxOccurrence: 3
     response:
       status: 200
       cookies:
    @@ -1491,51 +1516,60 @@ statically imported to your tests. Notice that the byComma
     the method name and passed the proper JSON path as a parameter to it.

    The resulting WireMock stub is in the following example:

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

    If you use a matcher, then the part of the request and response that the @@ -1876,11 +1910,16 @@ request: url: /users/1 response: status: 200 - --- request: method: POST url: /users/2 +response: + status: 200 +--- +request: + method: POST + url: /users/3 response: status: 200

    In the preceding example, one contract has the name field and the other does not. This diff --git a/multi/multi_spring-cloud-contract.html b/multi/multi_spring-cloud-contract.html index db8ea5337a..01950bd3ad 100644 --- a/multi/multi_spring-cloud-contract.html +++ b/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. History
    2.2. Why a Contract Verifier?
    2.2.1. Testing issues
    2.3. Purposes
    2.4. How It Works
    2.4.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.4.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.4.3. Defining the Contract
    2.4.4. Client Side
    2.4.5. Server Side
    2.5. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.5.1. Technical note
    2.5.2. Consumer side (Loan Issuance)
    2.5.3. Producer side (Fraud Detection server)
    2.5.4. Consumer Side (Loan Issuance) Final Step
    2.6. Dependencies
    2.7. Additional Links
    2.7.1. Spring Cloud Contract video
    2.7.2. Readings
    2.8. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    Keeping contracts with the producer and stubs in an external repository
    3.6.3. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.2.13. Maven Plugin with Spock Tests
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    4.5. Docker Project
    4.5.1. Short intro to Maven, JARs and Binary storage
    4.5.2. How it works
    Environment Variables
    4.5.3. Example of usage
    4.5.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule and Stub Runner JUnit5 Extension
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Camel
    7.2.1. Adding it to the project
    7.2.2. Disabling the functionality
    7.2.3. Examples
    Stubs structure
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Integration
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Stream
    7.4.1. Adding the Runner to the Project
    7.4.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.5. Stub Runner Spring AMQP
    7.5.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    13. Links
    \ No newline at end of file + Spring Cloud Contract

    Spring Cloud Contract


    Table of Contents

    1. Spring Cloud Contract
    2. Spring Cloud Contract Verifier Introduction
    2.1. History
    2.2. Why a Contract Verifier?
    2.2.1. Testing issues
    2.3. Purposes
    2.4. How It Works
    2.4.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.4.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.4.3. Defining the Contract
    2.4.4. Client Side
    2.4.5. Server Side
    2.5. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.5.1. Technical note
    2.5.2. Consumer side (Loan Issuance)
    2.5.3. Producer side (Fraud Detection server)
    2.5.4. Consumer Side (Loan Issuance) Final Step
    2.6. Dependencies
    2.7. Additional Links
    2.7.1. Spring Cloud Contract video
    2.7.2. Readings
    2.8. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    3.6.3. Producer with contracts stored locally
    Keeping contracts with the producer and stubs in an external repository
    3.6.4. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.2.13. Maven Plugin with Spock Tests
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    4.5. Docker Project
    4.5.1. Short intro to Maven, JARs and Binary storage
    4.5.2. How it works
    Environment Variables
    4.5.3. Example of usage
    4.5.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule and Stub Runner JUnit5 Extension
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Camel
    7.2.1. Adding it to the project
    7.2.2. Disabling the functionality
    7.2.3. Examples
    Stubs structure
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Integration
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Stream
    7.4.1. Adding the Runner to the Project
    7.4.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.5. Stub Runner Spring AMQP
    7.5.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    13. Links
    \ No newline at end of file diff --git a/single/spring-cloud-contract.html b/single/spring-cloud-contract.html index 2e20385a43..052c4e597a 100644 --- a/single/spring-cloud-contract.html +++ b/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. History
    2.2. Why a Contract Verifier?
    2.2.1. Testing issues
    2.3. Purposes
    2.4. How It Works
    2.4.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.4.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.4.3. Defining the Contract
    2.4.4. Client Side
    2.4.5. Server Side
    2.5. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.5.1. Technical note
    2.5.2. Consumer side (Loan Issuance)
    2.5.3. Producer side (Fraud Detection server)
    2.5.4. Consumer Side (Loan Issuance) Final Step
    2.6. Dependencies
    2.7. Additional Links
    2.7.1. Spring Cloud Contract video
    2.7.2. Readings
    2.8. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    Keeping contracts with the producer and stubs in an external repository
    3.6.3. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.2.13. Maven Plugin with Spock Tests
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    4.5. Docker Project
    4.5.1. Short intro to Maven, JARs and Binary storage
    4.5.2. How it works
    Environment Variables
    4.5.3. Example of usage
    4.5.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule and Stub Runner JUnit5 Extension
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Camel
    7.2.1. Adding it to the project
    7.2.2. Disabling the functionality
    7.2.3. Examples
    Stubs structure
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Integration
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Stream
    7.4.1. Adding the Runner to the Project
    7.4.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.5. Stub Runner Spring AMQP
    7.5.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    13. 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. History
    2.2. Why a Contract Verifier?
    2.2.1. Testing issues
    2.3. Purposes
    2.4. How It Works
    2.4.1. A Three-second Tour
    On the Producer Side
    On the Consumer Side
    2.4.2. A Three-minute Tour
    On the Producer Side
    On the Consumer Side
    2.4.3. Defining the Contract
    2.4.4. Client Side
    2.4.5. Server Side
    2.5. Step-by-step Guide to Consumer Driven Contracts (CDC)
    2.5.1. Technical note
    2.5.2. Consumer side (Loan Issuance)
    2.5.3. Producer side (Fraud Detection server)
    2.5.4. Consumer Side (Loan Issuance) Final Step
    2.6. Dependencies
    2.7. Additional Links
    2.7.1. Spring Cloud Contract video
    2.7.2. Readings
    2.8. Samples
    3. Spring Cloud Contract FAQ
    3.1. Why use Spring Cloud Contract Verifier and not X ?
    3.2. I don’t want to write a contract in Groovy!
    3.3. What is this value(consumer(), producer()) ?
    3.4. How to do Stubs versioning?
    3.4.1. API Versioning
    3.4.2. JAR versioning
    3.4.3. Dev or prod stubs
    3.5. Common repo with contracts
    3.5.1. Repo structure
    3.5.2. Workflow
    3.5.3. Consumer
    3.5.4. Producer
    3.5.5. How can I define messaging contracts per topic not per producer?
    For Maven Project
    For Gradle Project
    3.6. Do I need a Binary Storage? Can’t I use Git?
    3.6.1. Protocol convention
    3.6.2. Producer
    3.6.3. Producer with contracts stored locally
    Keeping contracts with the producer and stubs in an external repository
    3.6.4. Consumer
    3.7. Can I use the Pact Broker?
    3.7.1. Pact Consumer
    3.7.2. Producer
    3.7.3. Pact Consumer (Producer Contract approach)
    3.8. How can I debug the request/response being sent by the generated tests client?
    3.8.1. How can I debug the mapping/request/response being sent by WireMock?
    3.8.2. How can I see what got registered in the HTTP server stub?
    3.8.3. Can I reference text from file?
    4. Spring Cloud Contract Verifier Setup
    4.1. Gradle Project
    4.1.1. Prerequisites
    4.1.2. Add Gradle Plugin with Dependencies
    4.1.3. Gradle and Rest Assured 2.0
    4.1.4. Snapshot Versions for Gradle
    4.1.5. Add stubs
    4.1.6. Run the Plugin
    4.1.7. Default Setup
    4.1.8. Configure Plugin
    4.1.9. Configuration Options
    4.1.10. Single Base Class for All Tests
    4.1.11. Different Base Classes for Contracts
    4.1.12. Invoking Generated Tests
    4.1.13. Pushing stubs to SCM
    4.1.14. Spring Cloud Contract Verifier on the Consumer Side
    4.2. Maven Project
    4.2.1. Add maven plugin
    4.2.2. Maven and Rest Assured 2.0
    4.2.3. Snapshot versions for Maven
    4.2.4. Add stubs
    4.2.5. Run plugin
    4.2.6. Configure plugin
    4.2.7. Configuration Options
    4.2.8. Single Base Class for All Tests
    4.2.9. Different base classes for contracts
    4.2.10. Invoking generated tests
    4.2.11. Pushing stubs to SCM
    4.2.12. Maven Plugin and STS
    4.2.13. Maven Plugin with Spock Tests
    4.3. Stubs and Transitive Dependencies
    4.4. Scenarios
    4.5. Docker Project
    4.5.1. Short intro to Maven, JARs and Binary storage
    4.5.2. How it works
    Environment Variables
    4.5.3. Example of usage
    4.5.4. Server side (nodejs)
    5. Spring Cloud Contract Verifier Messaging
    5.1. Integrations
    5.2. Manual Integration Testing
    5.3. Publisher-Side Test Generation
    5.3.1. Scenario 1: No Input Message
    5.3.2. Scenario 2: Output Triggered by Input
    5.3.3. Scenario 3: No Output Message
    5.4. Consumer Stub Generation
    6. Spring Cloud Contract Stub Runner
    6.1. Snapshot versions
    6.2. Publishing Stubs as JARs
    6.3. Stub Runner Core
    6.3.1. Retrieving stubs
    Stub downloading
    Classpath scanning
    6.3.2. Running stubs
    Running using main app
    HTTP Stubs
    Viewing registered mappings
    Messaging Stubs
    6.4. Stub Runner JUnit Rule and Stub Runner JUnit5 Extension
    6.4.1. Maven settings
    6.4.2. Providing fixed ports
    6.4.3. Fluent API
    6.4.4. Stub Runner with Spring
    6.5. Stub Runner Spring Cloud
    6.5.1. Stubbing Service Discovery
    Test profiles and service discovery
    6.5.2. Additional Configuration
    6.6. Stub Runner Boot Application
    6.6.1. How to use it?
    Stub Runner Server
    Stub Runner Server Fat Jar
    Spring Cloud CLI
    6.6.2. Endpoints
    HTTP
    Messaging
    6.6.3. Example
    6.6.4. Stub Runner Boot with Service Discovery
    6.7. Stubs Per Consumer
    6.8. Common
    6.8.1. Common Properties for JUnit and Spring
    6.8.2. Stub Runner Stubs IDs
    6.9. Stub Runner Docker
    6.9.1. How to use it
    6.9.2. Example of client side usage in a non JVM project
    7. Stub Runner for Messaging
    7.1. Stub triggering
    7.1.1. Trigger by Label
    7.1.2. Trigger by Group and Artifact Ids
    7.1.3. Trigger by Artifact Ids
    7.1.4. Trigger All Messages
    7.2. Stub Runner Camel
    7.2.1. Adding it to the project
    7.2.2. Disabling the functionality
    7.2.3. Examples
    Stubs structure
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.3. Stub Runner Integration
    7.3.1. Adding the Runner to the Project
    7.3.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.4. Stub Runner Stream
    7.4.1. Adding the Runner to the Project
    7.4.2. Disabling the functionality
    Scenario 1 (no input message)
    Scenario 2 (output triggered by input)
    Scenario 3 (input with no output)
    7.5. Stub Runner Spring AMQP
    7.5.1. Adding the Runner to the Project
    Triggering the message
    Spring AMQP Test Configuration
    8. Contract DSL
    8.1. Limitations
    8.2. Common Top-Level elements
    8.2.1. Description
    8.2.2. Name
    8.2.3. Ignoring Contracts
    8.2.4. Passing Values from Files
    8.2.5. HTTP Top-Level Elements
    8.3. Request
    8.4. Response
    8.5. Dynamic properties
    8.5.1. Dynamic properties inside the body
    8.5.2. Regular expressions
    8.5.3. Passing Optional Parameters
    8.5.4. Executing Custom Methods on the Server Side
    8.5.5. Referencing the Request from the Response
    8.5.6. Registering Your Own WireMock Extension
    8.5.7. Dynamic Properties in the Matchers Sections
    8.6. JAX-RS Support
    8.7. Async Support
    8.8. Working with Context Paths
    8.9. Working with Web Flux
    8.10. Messaging Top-Level Elements
    8.10.1. Output Triggered by a Method
    8.10.2. Output Triggered by a Message
    8.10.3. Consumer/Producer
    8.10.4. Common
    8.11. Multiple Contracts in One File
    8.12. Generating Spring REST Docs snippets from the contracts
    9. Customization
    9.1. Extending the DSL
    9.1.1. Common JAR
    9.1.2. Adding the Dependency to the Project
    9.1.3. Test the Dependency in the Project’s Dependencies
    9.1.4. Test a Dependency in the Plugin’s Dependencies
    9.1.5. Referencing classes in DSLs
    10. Using the Pluggable Architecture
    10.1. Custom Contract Converter
    10.1.1. Pact Converter
    10.1.2. Pact Contract
    10.1.3. Pact for Producers
    10.1.4. Pact for Consumers
    10.2. Using the Custom Test Generator
    10.3. Using the Custom Stub Generator
    10.4. Using the Custom Stub Runner
    10.5. Using the Custom Stub Downloader
    10.6. Using the SCM Stub Downloader
    10.7. Using the Pact Stub Downloader
    11. Spring Cloud Contract WireMock
    11.1. Registering Stubs Automatically
    11.2. Using Files to Specify the Stub Bodies
    11.3. Alternative: Using JUnit Rules
    11.4. Relaxed SSL Validation for Rest Template
    11.5. WireMock and Spring MVC Mocks
    11.6. Customization of WireMock configuration
    11.7. Generating Stubs using REST Docs
    11.8. Generating Contracts by Using REST Docs
    12. Migrations
    12.1. 1.0.x → 1.1.x
    12.1.1. New structure of generated stubs
    12.2. 1.1.x → 1.2.x
    12.2.1. Custom HttpServerStub
    12.2.2. New packages for generated tests
    12.2.3. New Methods in TemplateProcessor
    12.2.4. RestAssured 3.0
    12.3. 1.2.x → 2.0.x
    13. Links

    Documentation Authors: Adam Dudczak, Mathias Düsterhöft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubryński, Karol Lassak, Olga Maciaszek-Sharma, Mariusz Smykuła, Dave Syer, Jay Bryant

    2.1.0.BUILD-SNAPSHOT

    1. Spring Cloud Contract

    You need confidence when pushing new features to a new application or service in a distributed system. This project provides support for Consumer Driven Contracts and service schemas in Spring applications (for both HTTP and message-based interactions), @@ -643,6 +643,7 @@ First, add the Spring Cloud Contract BOM.

    <extensions>true</extensions>
     	<configuration>
     		<packageWithBaseClasses>com.example.fraud</packageWithBaseClasses>
    +		<convertToYaml>true</convertToYaml>
     	</configuration>
     </plugin>

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

    • generate and run tests
    • produce and install stubs

    You do not want to generate tests since you, as the consumer, want only to play with the @@ -708,6 +709,7 @@ $ git pull https://your-git-server.com/server-side-fork.git contract-change-pr<extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> + <convertToYaml>true</convertToYaml> </configuration> </plugin>

    [Important]Important

    This example uses "convention based" naming by setting the packageWithBaseClasses property. Doing so means that the two last packages combine to @@ -1252,10 +1254,95 @@ the `publish` task is executed publish.dependsOn("publishStubsToScm")

    With such a setup:

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

    Keeping contracts with the producer and stubs in an external repository

    It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo. +META-INF/com.example/foo/1.0.0/contracts

  • Tests will be generated from the contracts
  • Stubs will be created from the contracts
  • Once the tests pass, the stubs will be committed in the cloned repository
  • Finally, a push will be done to that repo’s origin
  • 3.6.3 Producer with contracts stored locally

    Another option to use the SCM as the destination for stubs and contracts is to store the contracts locally, with the producer, and only push the contracts and the stubs to SCM. Below, you can find the setup required to achieve this using Maven and Gradle.

    Maven.  +

    <plugin>
    +	<groupId>org.springframework.cloud</groupId>
    +	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    +	<version>${spring-cloud-contract.version}</version>
    +	<extensions>true</extensions>
    +	<!-- In the default configuration, we want to use the contracts stored locally -->
    +	<configuration>
    +		<baseClassMappings>
    +			<baseClassMapping>
    +				<contractPackageRegex>.*messaging.*</contractPackageRegex>
    +				<baseClassFQN>com.example.BeerMessagingBase</baseClassFQN>
    +			</baseClassMapping>
    +			<baseClassMapping>
    +				<contractPackageRegex>.*rest.*</contractPackageRegex>
    +				<baseClassFQN>com.example.BeerRestBase</baseClassFQN>
    +			</baseClassMapping>
    +		</baseClassMappings>
    +		<basePackageForTests>com.example</basePackageForTests>
    +	</configuration>
    +	<executions>
    +		<execution>
    +			<phase>package</phase>
    +			<goals>
    +				<!-- By default we will not push the stubs back to SCM,
    +				you have to explicitly add it as a goal -->
    +				<goal>pushStubsToScm</goal>
    +			</goals>
    +			<configuration>
    +				<!-- We want to pick contracts from a Git repository -->
    +				<contractsRepositoryUrl>git://file://${env.ROOT}/target/contract_empty_git/</contractsRepositoryUrl>
    +				<!-- Example of URL via git protocol -->
    +				<!--<contractsRepositoryUrl>git://git@github.com:spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>-->
    +				<!-- Example of URL via http protocol -->
    +				<!--<contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>-->
    +				<!-- We reuse the contract dependency section to set up the path
    +				to the folder that contains the contract definitions. In our case the
    +				path will be /groupId/artifactId/version/contracts -->
    +				<contractDependency>
    +					<groupId>${project.groupId}</groupId>
    +					<artifactId>${project.artifactId}</artifactId>
    +					<version>${project.version}</version>
    +				</contractDependency>
    +				<!-- The mode can't be classpath -->
    +				<contractsMode>LOCAL</contractsMode>
    +			</configuration>
    +		</execution>
    +	</executions>
    +</plugin>

    +

    Gradle.  +

    contracts {
    +		// Base package for generated tests
    +	basePackageForTests = "com.example"
    +	baseClassMappings {
    +		baseClassMapping(".*messaging.*", "com.example.BeerMessagingBase")
    +		baseClassMapping(".*rest.*", "com.example.BeerRestBase")
    +	}
    +}
    +
    +/*
    +In this scenario we want to publish stubs to SCM whenever
    +the `publish` task is executed
    +*/
    +publishStubsToScm {
    +	// We want to modify the default set up of the plugin when publish stubs to scm is called
    +	customize {
    +		// We want to pick contracts from a Git repository
    +		contractDependency {
    +			stringNotation = "${project.group}:${project.name}:${project.version}"
    +		}
    +		/*
    +		We reuse the contract dependency section to set up the path
    +		to the folder that contains the contract definitions. In our case the
    +		path will be /groupId/artifactId/version/contracts
    +		 */
    +		contractRepository {
    +			repositoryUrl = "git://file://${System.getenv("ROOT")}/target/contract_empty_git/"
    +		}
    +		// The mode can't be classpath
    +		contractsMode = "LOCAL"
    +	}
    +}
    +
    +publish.dependsOn("publishStubsToScm")
    +publishToMavenLocal.dependsOn("publishStubsToScm")

    +

    With such a setup:

    • Contracts from the default src/test/resources/contracts directory will be picked
    • Tests will be generated from the contracts
    • Stubs will be created from the contracts
    • Once the tests pass

      • Git project will be cloned to a temporary directory
      • The stubs and contracts will be committed in the cloned repository
    • Finally, a push will be done to that repo’s origin

    Keeping contracts with the producer and stubs in an external repository

    It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo. This is most useful when you want to use the base consumer-producer collaboration flow, but do not have a possibility to use an artifact repository for storing the stubs.

    In order to do that, use the usual producer setup, and then add the pushStubsToScm goal and set -contractsRepositoryUrl to the repository where you want to keep the stubs.

    3.6.3 Consumer

    On the consumer side when passing the repositoryRoot parameter, +contractsRepositoryUrl to the repository where you want to keep the stubs.

    3.6.4 Consumer

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

    @AutoConfigureStubRunner(
    @@ -1545,7 +1632,7 @@ closure to set it up.
  • cont downloaded, the path defaults to groupid/artifactid where groupid is slash separated. Otherwise, it scans contracts under the provided directory.
  • contractsMode: Specifies the mode of downloading contracts (whether the JAR is available offline, remotely etc.)
  • deleteStubsAfterTest: If set to false will not remove any downloaded -contracts from temporary directories
  • 4.1.10 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base +contracts from temporary directories

    Below you can find a list of experimental features you can turn on via the plugin:

    • convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side.
    • assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default.

    4.2.8 Single Base Class for All Tests

    When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base ++ or 1.0.0.BUILD-SNAPSHOT won’t get cached). By default, this feature is turned on.

    Below you can find a list of experimental features you can turn on via the plugin:

    • convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side.
    • assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default.

    4.2.8 Single Base Class for All Tests

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

    package org.mycompany.tests
     
    @@ -4109,80 +4197,80 @@ use in your contracts, as shown in the following example:

    return Pattern.compile(values.collect({"^$it\$"}).join("|"))
     }
     
    -Pattern onlyAlphaUnicode() {
    -	return ONLY_ALPHA_UNICODE
    +RegexProperty onlyAlphaUnicode() {
    +	return new RegexProperty(ONLY_ALPHA_UNICODE).asString()
     }
     
    -Pattern alphaNumeric() {
    -	return ALPHA_NUMERIC
    +RegexProperty alphaNumeric() {
    +	return new RegexProperty(ALPHA_NUMERIC).asString()
     }
     
    -Pattern number() {
    -	return NUMBER
    +RegexProperty number() {
    +	return new RegexProperty(NUMBER).asDouble()
     }
     
    -Pattern positiveInt() {
    -	return POSITIVE_INT
    +RegexProperty positiveInt() {
    +	return new RegexProperty(POSITIVE_INT).asInteger()
     }
     
    -Pattern anyBoolean() {
    -	return TRUE_OR_FALSE
    +RegexProperty anyBoolean() {
    +	return new RegexProperty(TRUE_OR_FALSE).asBooleanType()
     }
     
    -Pattern anInteger() {
    -	return INTEGER
    +RegexProperty anInteger() {
    +	return new RegexProperty(INTEGER).asInteger()
     }
     
    -Pattern aDouble() {
    -	return DOUBLE
    +RegexProperty aDouble() {
    +	return new RegexProperty(DOUBLE).asDouble()
     }
     
    -Pattern ipAddress() {
    -	return IP_ADDRESS
    +RegexProperty ipAddress() {
    +	return new RegexProperty(IP_ADDRESS).asString()
     }
     
    -Pattern hostname() {
    -	return HOSTNAME_PATTERN
    +RegexProperty hostname() {
    +	return new RegexProperty(HOSTNAME_PATTERN).asString()
     }
     
    -Pattern email() {
    -	return EMAIL
    +RegexProperty email() {
    +	return new RegexProperty(EMAIL).asString()
     }
     
    -Pattern url() {
    -	return URL
    +RegexProperty url() {
    +	return new RegexProperty(URL).asString()
     }
     
    -Pattern httpsUrl() {
    -	return HTTPS_URL
    +RegexProperty httpsUrl() {
    +	return new RegexProperty(HTTPS_URL).asString()
     }
     
    -Pattern uuid(){
    -	return UUID
    +RegexProperty uuid(){
    +	return new RegexProperty(UUID).asString()
     }
     
    -Pattern isoDate() {
    -	return ANY_DATE
    +RegexProperty isoDate() {
    +	return new RegexProperty(ANY_DATE).asString()
     }
     
    -Pattern isoDateTime() {
    -	return ANY_DATE_TIME
    +RegexProperty isoDateTime() {
    +	return new RegexProperty(ANY_DATE_TIME).asString()
     }
     
    -Pattern isoTime() {
    -	return ANY_TIME
    +RegexProperty isoTime() {
    +	return new RegexProperty(ANY_TIME).asString()
     }
     
    -Pattern iso8601WithOffset() {
    -	return ISO8601_WITH_OFFSET
    +RegexProperty iso8601WithOffset() {
    +	return new RegexProperty(ISO8601_WITH_OFFSET).asString()
     }
     
    -Pattern nonEmpty() {
    -	return NON_EMPTY
    +RegexProperty nonEmpty() {
    +	return new RegexProperty(NON_EMPTY).asString()
     }
     
    -Pattern nonBlank() {
    -	return NON_BLANK
    +RegexProperty nonBlank() {
    +	return new RegexProperty(NON_BLANK).asString()
     }

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

    Contract dslWithOptionalsInString = Contract.make {
         priority 1
         request {
    @@ -4562,7 +4650,7 @@ This section is present in the response or 

    Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the following matching possibilities:

    Groovy DSL

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

      • byEquality(): The value taken from the consumer’s request via the provided JSON Path must be equal to the value provided in the contract.
      • byRegex(…​): The value taken from the consumer’s request via the provided JSON Path must -match the regex.
      • byDate(): The value taken from the consumer’s request via the provided JSON Path must +match the regex. You can also pass the type of the expected matched value (e.g. asString(), asLong() etc.)
      • byDate(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO Date value.
      • byTimestamp(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO DateTime value.
      • byTime(): The value taken from the consumer’s request via the provided JSON Path must match the regex for an ISO Time value.
    • For the verification(in generated tests on the Producer’s side):

      • byEquality(): The value taken from the producer’s response via the provided JSON Path must be @@ -4572,7 +4660,7 @@ the regex for an ISO Date value.
      • match the regex for an ISO DateTime value.
      • byTime(): The value taken from the producer’s response via the provided JSON Path must match the regex for an ISO Time value.
      • byType(): The value taken from the producer’s response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. -byType can take a closure, in which you can set minOccurrence and maxOccurrence. +byType can take a closure, in which you can set minOccurrence and maxOccurrence. For the request side, you should use the closure to assert size of the collection. 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 producer’s response via the provided JSON Path is passed as an input to the custom method that you provide. For example, @@ -4581,11 +4669,12 @@ JSON Path gets passed. The type of the object read from the JSON can be one of t following, depending on the JSON path:

        • String: If you point to a String value.
        • JSONArray: If you point to a List.
        • Map: If you point to a Map.
        • Number: If you point to Integer, Double, or other kind of number.
        • Boolean: If you point to a Boolean.
      • byNull(): The value taken from the response via the provided JSON Path must be null

    YAML. Please read the Groovy section for detailed explanation of what the types mean

    For YAML the structure of a matcher looks like this

    - path: $.foo
       type: by_regex
    -  value: bar

    Or if you want to use one of the predefined regular expressions + value: bar + regexType: as_string

    Or if you want to use one of the predefined regular expressions [only_alpha_unicode, number, any_boolean, ip_address, hostname, email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]:

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

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

    • For stubMatchers:

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

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

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

    Consider the following example:

    Groovy DSL.  + predefined: only_alpha_unicode

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

    • For stubMatchers:

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

        • there are 2 additional fields accepted: minOccurrence and maxOccurrence.
    • For testMatchers:

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

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

    You can also define which type the regular expression corresponds to via the regexType field. Below you can find the allowed list of regular expression types:

    • as_integer
    • as_double
    • as_float,
    • as_long
    • as_short
    • as_boolean
    • as_string

    Consider the following example:

    Groovy DSL. 

    Contract contractDsl = Contract.make {
     	request {
     		method 'GET'
    @@ -4605,12 +4694,12 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
     				]
     		])
     		bodyMatchers {
    -			jsonPath('$.duck', byRegex("[0-9]{3}"))
    +			jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
     			jsonPath('$.duck', byEquality())
    -			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
    +			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
     			jsonPath('$.alpha', byEquality())
    -			jsonPath('$.number', byRegex(number()))
    -			jsonPath('$.aBoolean', byRegex(anyBoolean()))
    +			jsonPath('$.number', byRegex(number()).asInteger())
    +			jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
     			jsonPath('$.date', byDate())
     			jsonPath('$.dateTime', byTimestamp())
     			jsonPath('$.time', byTime())
    @@ -4654,18 +4743,18 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
     		])
     		bodyMatchers {
     			// asserts the jsonpath value against manual regex
    -			jsonPath('$.duck', byRegex("[0-9]{3}"))
    +			jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
     			// asserts the jsonpath value against the provided value
     			jsonPath('$.duck', byEquality())
     			// asserts the jsonpath value against some default regex
    -			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
    +			jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
     			jsonPath('$.alpha', byEquality())
    -			jsonPath('$.number', byRegex(number()))
    -			jsonPath('$.positiveInteger', byRegex(anInteger()))
    -			jsonPath('$.negativeInteger', byRegex(anInteger()))
    -			jsonPath('$.positiveDecimalNumber', byRegex(aDouble()))
    -			jsonPath('$.negativeDecimalNumber', byRegex(aDouble()))
    -			jsonPath('$.aBoolean', byRegex(anyBoolean()))
    +			jsonPath('$.number', byRegex(number()).asInteger())
    +			jsonPath('$.positiveInteger', byRegex(anInteger()).asInteger())
    +			jsonPath('$.negativeInteger', byRegex(anInteger()).asInteger())
    +			jsonPath('$.positiveDecimalNumber', byRegex(aDouble()).asDouble())
    +			jsonPath('$.negativeDecimalNumber', byRegex(aDouble()).asDouble())
    +			jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
     			// asserts vs inbuilt time related regex
     			jsonPath('$.date', byDate())
     			jsonPath('$.dateTime', byTimestamp())
    @@ -4735,6 +4824,20 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
         key:
           "complex.key": 'foo'
         nullValue: null
    +    valueWithMin:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinMax:
    +      - 1
    +      - 2
    +      - 3
    +    valueWithMinEmpty: []
    +    valueWithMaxEmpty: []
       matchers:
         url:
           regex: /get/[0-9]
    @@ -4797,6 +4900,16 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e
             type: by_equality
           - path: $.nullvalue
             type: by_null
    +      - path: $.valueWithMin
    +        type: by_type
    +        minOccurrence: 1
    +      - path: $.valueWithMax
    +        type: by_type
    +        maxOccurrence: 3
    +      - path: $.valueWithMinMax
    +        type: by_type
    +        minOccurrence: 1
    +        maxOccurrence: 3
     response:
       status: 200
       cookies:
    @@ -4944,51 +5057,60 @@ statically imported to your tests. Notice that the byComma
     the method name and passed the proper JSON path as a parameter to it.

    The resulting WireMock stub is in the following example:

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

    If you use a matcher, then the part of the request and response that the @@ -5329,11 +5451,16 @@ request: url: /users/1 response: status: 200 - --- request: method: POST url: /users/2 +response: + status: 200 +--- +request: + method: POST + url: /users/3 response: status: 200

    In the preceding example, one contract has the name field and the other does not. This @@ -5620,7 +5747,7 @@ visible in your Groovy files. The following examples show how to test the depend <scope>test</scope> </dependency>

    Gradle.  -

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

    +

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

    9.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>
    @@ -5647,7 +5774,7 @@ following example:

    Maven.  </dependencies> </plugin>

    Gradle.  -

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

    +

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

    9.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
    @@ -5689,7 +5816,7 @@ then:
     			contentType(applicationJson())
     		}
     	}
    -}

    10. Using the Pluggable Architecture

    You may encounter cases where you have your contracts have been defined in other formats, +}

    [Important]Important

    You can set the Spring Cloud Contract plugin up by setting convertToYaml to true. That way you will NOT have to add the dependency with the extended functionality to the consumer side, since the consumer side will be using YAML contracts instead of Groovy ones.

    10. 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 @@ -6127,13 +6254,13 @@ to clone the repository and use it as a source of contracts to generate tests or stubs.

    Either via environment variables, system properties, properties set inside the plugin or contracts repository configuration you can tweak the downloader’s behaviour. Below you can find the list of -properties

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop)

    * stubrunner.properties.git.branch (system prop)

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop)

    * stubrunner.properties.git.username (system prop)

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop)

    * stubrunner.properties.git.password (system prop)

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop)

    * stubrunner.properties.git.no-of-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop)

    * stubrunner.properties.git.wait-between-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol +properties

    Table 10.1. SCM Stub Downloader properties

    Type of a property

    Name of the property

    Description

    * git.branch (plugin prop)

    * stubrunner.properties.git.branch (system prop)

    * STUBRUNNER_PROPERTIES_GIT_BRANCH (env prop)

    master

    Which branch to checkout

    * git.username (plugin prop)

    * stubrunner.properties.git.username (system prop)

    * STUBRUNNER_PROPERTIES_GIT_USERNAME (env prop)

     

    Git clone username

    * git.password (plugin prop)

    * stubrunner.properties.git.password (system prop)

    * STUBRUNNER_PROPERTIES_GIT_PASSWORD (env prop)

     

    Git clone password

    * git.no-of-attempts (plugin prop)

    * stubrunner.properties.git.no-of-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS (env prop)

    10

    Number of attempts to push the commits to origin

    * git.wait-between-attempts (Plugin prop)

    * stubrunner.properties.git.wait-between-attempts (system prop)

    * STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS (env prop)

    1000

    Number of millis to wait between attempts to push the commits to origin


    10.7 Using the Pact Stub Downloader

    Whenever the repositoryRoot starts with a Pact protocol (starts with pact://), the stub downloader will try to fetch the Pact contract definitions from the Pact Broker. Whatever is set after pact:// will be parsed as the Pact Broker URL.

    Either via environment variables, system properties, properties set inside the plugin or contracts repository configuration you can tweak the downloader’s behaviour. Below you can find the list of -properties

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop)

    * stubrunner.properties.pactbroker.host (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop)

    * stubrunner.properties.pactbroker.port (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop)

    * stubrunner.properties.pactbroker.protocol (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop)

    * stubrunner.properties.pactbroker.tags (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop)

    * stubrunner.properties.pactbroker.auth.scheme (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop)

    * stubrunner.properties.pactbroker.auth.username (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop)

    * stubrunner.properties.pactbroker.auth.password (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop)

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    11. Spring Cloud Contract WireMock

    The Spring Cloud Contract WireMock modules let you use WireMock in a +properties

    Table 10.2. SCM Stub Downloader properties

    Name of a property

    Default

    Description

    * pactbroker.host (plugin prop)

    * stubrunner.properties.pactbroker.host (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_HOST (env prop)

    Host from URL passed to repositoryRoot

    What is the URL of Pact Broker

    * pactbroker.port (plugin prop)

    * stubrunner.properties.pactbroker.port (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PORT (env prop)

    Port from URL passed to repositoryRoot

    What is the port of Pact Broker

    * pactbroker.protocol (plugin prop)

    * stubrunner.properties.pactbroker.protocol (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL (env prop)

    Protocol from URL passed to repositoryRoot

    What is the protocol of Pact Broker

    * pactbroker.tags (plugin prop)

    * stubrunner.properties.pactbroker.tags (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_TAGS (env prop)

    Version of the stub, or latest if version is +

    What tags should be used to fetch the stub

    * pactbroker.auth.scheme (plugin prop)

    * stubrunner.properties.pactbroker.auth.scheme (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME (env prop)

    Basic

    What kind of authentication should be used to connect to the Pact Broker

    * pactbroker.auth.username (plugin prop)

    * stubrunner.properties.pactbroker.auth.username (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME (env prop)

    The username passed to contractsRepositoryUsername (maven) or contractRepository.username (gradle)

    Username used to connect to the Pact Broker

    * pactbroker.auth.password (plugin prop)

    * stubrunner.properties.pactbroker.auth.password (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD (env prop)

    The password passed to contractsRepositoryPassword (maven) or contractRepository.password (gradle)

    Password used to connect to the Pact Broker

    * pactbroker.provider-name-with-group-id (plugin prop)

    * stubrunner.properties.pactbroker.provider-name-with-group-id (system prop)

    * STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID (env prop)

    false

    When true, the provider name will be a combination of groupId:artifactId. If false, just artifactId is used


    11. Spring Cloud Contract WireMock

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

    If you have a Spring Boot application that uses Tomcat as an embedded server (which is diff --git a/spring-cloud-contract.xml b/spring-cloud-contract.xml index 28859838d8..0b762a55a7 100644 --- a/spring-cloud-contract.xml +++ b/spring-cloud-contract.xml @@ -4,7 +4,7 @@ Spring Cloud Contract -2018-12-12 +2018-11-18 @@ -1082,6 +1082,7 @@ First, add the Spring Cloud Contract BOM. <extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> + <convertToYaml>true</convertToYaml> </configuration> </plugin> Since the plugin was added, you get the Spring Cloud Contract Verifier features which, @@ -1190,6 +1191,7 @@ $ git pull https://your-git-server.com/server-side-fork.git contract-change-pr @@ -2066,6 +2068,129 @@ to find contracts. E.g. for com.example:foo:1.0.0 the path wo Finally, a push will be done to that repo’s origin + +

    +Producer with contracts stored locally +Another option to use the SCM as the destination for stubs and contracts is to store the contracts locally, with the producer, and only push the contracts and the stubs to SCM. Below, you can find the setup required to achieve this using Maven and Gradle. + +Maven + +<plugin> + <groupId>org.springframework.cloud</groupId> + <artifactId>spring-cloud-contract-maven-plugin</artifactId> + <version>${spring-cloud-contract.version}</version> + <extensions>true</extensions> + <!-- In the default configuration, we want to use the contracts stored locally --> + <configuration> + <baseClassMappings> + <baseClassMapping> + <contractPackageRegex>.*messaging.*</contractPackageRegex> + <baseClassFQN>com.example.BeerMessagingBase</baseClassFQN> + </baseClassMapping> + <baseClassMapping> + <contractPackageRegex>.*rest.*</contractPackageRegex> + <baseClassFQN>com.example.BeerRestBase</baseClassFQN> + </baseClassMapping> + </baseClassMappings> + <basePackageForTests>com.example</basePackageForTests> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <!-- By default we will not push the stubs back to SCM, + you have to explicitly add it as a goal --> + <goal>pushStubsToScm</goal> + </goals> + <configuration> + <!-- We want to pick contracts from a Git repository --> + <contractsRepositoryUrl>git://file://${env.ROOT}/target/contract_empty_git/</contractsRepositoryUrl> + <!-- Example of URL via git protocol --> + <!--<contractsRepositoryUrl>git://git@github.com:spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>--> + <!-- Example of URL via http protocol --> + <!--<contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>--> + <!-- We reuse the contract dependency section to set up the path + to the folder that contains the contract definitions. In our case the + path will be /groupId/artifactId/version/contracts --> + <contractDependency> + <groupId>${project.groupId}</groupId> + <artifactId>${project.artifactId}</artifactId> + <version>${project.version}</version> + </contractDependency> + <!-- The mode can't be classpath --> + <contractsMode>LOCAL</contractsMode> + </configuration> + </execution> + </executions> +</plugin> + + + +Gradle + +contracts { + // Base package for generated tests + basePackageForTests = "com.example" + baseClassMappings { + baseClassMapping(".*messaging.*", "com.example.BeerMessagingBase") + baseClassMapping(".*rest.*", "com.example.BeerRestBase") + } +} + +/* +In this scenario we want to publish stubs to SCM whenever +the `publish` task is executed +*/ +publishStubsToScm { + // We want to modify the default set up of the plugin when publish stubs to scm is called + customize { + // We want to pick contracts from a Git repository + contractDependency { + stringNotation = "${project.group}:${project.name}:${project.version}" + } + /* + We reuse the contract dependency section to set up the path + to the folder that contains the contract definitions. In our case the + path will be /groupId/artifactId/version/contracts + */ + contractRepository { + repositoryUrl = "git://file://${System.getenv("ROOT")}/target/contract_empty_git/" + } + // The mode can't be classpath + contractsMode = "LOCAL" + } +} + +publish.dependsOn("publishStubsToScm") +publishToMavenLocal.dependsOn("publishStubsToScm") + + +With such a setup: + + +Contracts from the default src/test/resources/contracts directory will be picked + + +Tests will be generated from the contracts + + +Stubs will be created from the contracts + + +Once the tests pass + + +Git project will be cloned to a temporary directory + + +The stubs and contracts will be committed in the cloned repository + + + + +Finally, a push will be done to that repo’s origin + +
    Keeping contracts with the producer and stubs in an external repository It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo. @@ -2644,6 +2769,15 @@ JAR is available offline, remotely etc.) contracts from temporary directories +Below you can find a list of experimental features you can turn on via the plugin: + + +convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side. + + +assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default. + +
    Single Base Class for All Tests @@ -2827,6 +2961,7 @@ following sections: <extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> + <convertToYaml>true</convertToYaml> </configuration> </plugin> You can read more in the @@ -3089,6 +3224,15 @@ use the current Maven ones. We cache only non-snapshot, explicitly provided versions (for example + or 1.0.0.BUILD-SNAPSHOT won’t get cached). By default, this feature is turned on. +Below you can find a list of experimental features you can turn on via the plugin: + + +convertToYaml: converts all DSLs to the declarative, YAML format. This can be extremely useful when you’re using external libraries in your Groovy DSLs. By turning this feature on (by setting it to true) you will not need to add the library dependency on the consumer side. + + +assertJsonSize: You can check the size of JSON arrays in the generated tests. This feature is disabled by default. + +
    Single Base Class for All Tests @@ -6894,80 +7038,80 @@ protected static Pattern anyOf(String... values){ return Pattern.compile(values.collect({"^$it\$"}).join("|")) } -Pattern onlyAlphaUnicode() { - return ONLY_ALPHA_UNICODE +RegexProperty onlyAlphaUnicode() { + return new RegexProperty(ONLY_ALPHA_UNICODE).asString() } -Pattern alphaNumeric() { - return ALPHA_NUMERIC +RegexProperty alphaNumeric() { + return new RegexProperty(ALPHA_NUMERIC).asString() } -Pattern number() { - return NUMBER +RegexProperty number() { + return new RegexProperty(NUMBER).asDouble() } -Pattern positiveInt() { - return POSITIVE_INT +RegexProperty positiveInt() { + return new RegexProperty(POSITIVE_INT).asInteger() } -Pattern anyBoolean() { - return TRUE_OR_FALSE +RegexProperty anyBoolean() { + return new RegexProperty(TRUE_OR_FALSE).asBooleanType() } -Pattern anInteger() { - return INTEGER +RegexProperty anInteger() { + return new RegexProperty(INTEGER).asInteger() } -Pattern aDouble() { - return DOUBLE +RegexProperty aDouble() { + return new RegexProperty(DOUBLE).asDouble() } -Pattern ipAddress() { - return IP_ADDRESS +RegexProperty ipAddress() { + return new RegexProperty(IP_ADDRESS).asString() } -Pattern hostname() { - return HOSTNAME_PATTERN +RegexProperty hostname() { + return new RegexProperty(HOSTNAME_PATTERN).asString() } -Pattern email() { - return EMAIL +RegexProperty email() { + return new RegexProperty(EMAIL).asString() } -Pattern url() { - return URL +RegexProperty url() { + return new RegexProperty(URL).asString() } -Pattern httpsUrl() { - return HTTPS_URL +RegexProperty httpsUrl() { + return new RegexProperty(HTTPS_URL).asString() } -Pattern uuid(){ - return UUID +RegexProperty uuid(){ + return new RegexProperty(UUID).asString() } -Pattern isoDate() { - return ANY_DATE +RegexProperty isoDate() { + return new RegexProperty(ANY_DATE).asString() } -Pattern isoDateTime() { - return ANY_DATE_TIME +RegexProperty isoDateTime() { + return new RegexProperty(ANY_DATE_TIME).asString() } -Pattern isoTime() { - return ANY_TIME +RegexProperty isoTime() { + return new RegexProperty(ANY_TIME).asString() } -Pattern iso8601WithOffset() { - return ISO8601_WITH_OFFSET +RegexProperty iso8601WithOffset() { + return new RegexProperty(ISO8601_WITH_OFFSET).asString() } -Pattern nonEmpty() { - return NON_EMPTY +RegexProperty nonEmpty() { + return new RegexProperty(NON_EMPTY).asString() } -Pattern nonBlank() { - return NON_BLANK +RegexProperty nonBlank() { + return new RegexProperty(NON_BLANK).asString() } In your contract, you can use it as shown in the following example: Contract dslWithOptionalsInString = Contract.make { @@ -7539,7 +7683,7 @@ equal to the value provided in the contract. byRegex(…​): The value taken from the consumer’s request via the provided JSON Path must -match the regex. +match the regex. You can also pass the type of the expected matched value (e.g. asString(), asLong() etc.) byDate(): The value taken from the consumer’s request via the provided JSON Path must @@ -7581,7 +7725,7 @@ the regex for an ISO Time value. byType(): The value taken from the producer’s response via the provided JSON Path needs to be of the same type as the type defined in the body of the response in the contract. -byType can take a closure, in which you can set minOccurrence and maxOccurrence. +byType can take a closure, in which you can set minOccurrence and maxOccurrence. For the request side, you should use the closure to assert size of the collection. 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. @@ -7623,7 +7767,8 @@ what the types mean For YAML the structure of a matcher looks like this - path: $.foo type: by_regex - value: bar + value: bar + regexType: as_string Or if you want to use one of the predefined regular expressions [only_alpha_unicode, number, any_boolean, ip_address, hostname, email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]: @@ -7650,6 +7795,14 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e by_time + +by_type + + +there are 2 additional fields accepted: minOccurrence and maxOccurrence. + + + @@ -7687,6 +7840,30 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e +You can also define which type the regular expression corresponds to via the regexType field. Below you can find the allowed list of regular expression types: + + +as_integer + + +as_double + + +as_float, + + +as_long + + +as_short + + +as_boolean + + +as_string + + Consider the following example: Groovy DSL @@ -7710,12 +7887,12 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e ] ]) bodyMatchers { - jsonPath('$.duck', byRegex("[0-9]{3}")) + jsonPath('$.duck', byRegex("[0-9]{3}").asInteger()) jsonPath('$.duck', byEquality()) - jsonPath('$.alpha', byRegex(onlyAlphaUnicode())) + jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString()) jsonPath('$.alpha', byEquality()) - jsonPath('$.number', byRegex(number())) - jsonPath('$.aBoolean', byRegex(anyBoolean())) + jsonPath('$.number', byRegex(number()).asInteger()) + jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType()) jsonPath('$.date', byDate()) jsonPath('$.dateTime', byTimestamp()) jsonPath('$.time', byTime()) @@ -7759,18 +7936,18 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e ]) bodyMatchers { // asserts the jsonpath value against manual regex - jsonPath('$.duck', byRegex("[0-9]{3}")) + jsonPath('$.duck', byRegex("[0-9]{3}").asInteger()) // asserts the jsonpath value against the provided value jsonPath('$.duck', byEquality()) // asserts the jsonpath value against some default regex - jsonPath('$.alpha', byRegex(onlyAlphaUnicode())) + jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString()) jsonPath('$.alpha', byEquality()) - jsonPath('$.number', byRegex(number())) - jsonPath('$.positiveInteger', byRegex(anInteger())) - jsonPath('$.negativeInteger', byRegex(anInteger())) - jsonPath('$.positiveDecimalNumber', byRegex(aDouble())) - jsonPath('$.negativeDecimalNumber', byRegex(aDouble())) - jsonPath('$.aBoolean', byRegex(anyBoolean())) + jsonPath('$.number', byRegex(number()).asInteger()) + jsonPath('$.positiveInteger', byRegex(anInteger()).asInteger()) + jsonPath('$.negativeInteger', byRegex(anInteger()).asInteger()) + jsonPath('$.positiveDecimalNumber', byRegex(aDouble()).asDouble()) + jsonPath('$.negativeDecimalNumber', byRegex(aDouble()).asDouble()) + jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType()) // asserts vs inbuilt time related regex jsonPath('$.date', byDate()) jsonPath('$.dateTime', byTimestamp()) @@ -7844,6 +8021,20 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e key: "complex.key": 'foo' nullValue: null + valueWithMin: + - 1 + - 2 + - 3 + valueWithMax: + - 1 + - 2 + - 3 + valueWithMinMax: + - 1 + - 2 + - 3 + valueWithMinEmpty: [] + valueWithMaxEmpty: [] matchers: url: regex: /get/[0-9] @@ -7906,6 +8097,16 @@ email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_e type: by_equality - path: $.nullvalue type: by_null + - path: $.valueWithMin + type: by_type + minOccurrence: 1 + - path: $.valueWithMax + type: by_type + maxOccurrence: 3 + - path: $.valueWithMinMax + type: by_type + minOccurrence: 1 + maxOccurrence: 3 response: status: 200 cookies: @@ -8077,51 +8278,60 @@ the method name and passed the proper JSON path as a parameter to it. ''' { "request" : { - "urlPath" : "/get", - "method" : "POST", - "headers" : { - "Content-Type" : { - "matches" : "application/json.*" - } - }, - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]" - }, { - "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]" - }, { - "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]" - }, { - "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]" - }, { - "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]" - }, { - "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]" - }, { - "matchesJsonPath" : "$[?(@.duck == 123)]" - }, { - "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]" - }, { - "matchesJsonPath" : "$[?(@.alpha == 'abc')]" - }, { - "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]" - }, { - "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]" - }, { - "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]" - }, { - "matchesJsonPath" : "$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" - }, { - "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" - }, { - "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]" - } ] + "urlPath" : "/get", + "method" : "POST", + "headers" : { + "Content-Type" : { + "matches" : "application/json.*" + } + }, + "bodyPatterns" : [ { + "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]" + }, { + "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]" + }, { + "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]" + }, { + "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]" + }, { + "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]" + }, { + "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]" + }, { + "matchesJsonPath" : "$[?(@.duck == 123)]" + }, { + "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]" + }, { + "matchesJsonPath" : "$[?(@.alpha == 'abc')]" + }, { + "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]" + }, { + "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]" + }, { + "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]" + }, { + "matchesJsonPath" : "$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" + }, { + "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" + }, { + "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]" + }, { + "matchesJsonPath" : "$[?(@.valueWithMin.size() >= 1)]" + }, { + "matchesJsonPath" : "$[?(@.valueWithMax.size() <= 3)]" + }, { + "matchesJsonPath" : "$[?(@.valueWithMinMax.size() >= 1 && @.valueWithMinMax.size() <= 3)]" + }, { + "matchesJsonPath" : "$[?(@.valueWithOccurrence.size() >= 4 && @.valueWithOccurrence.size() <= 4)]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}", - "headers" : { - "Content-Type" : "application/json" - } + "status" : 200, + "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"aBoolean\\":true,\\"valueWithMax\\":[1,2,3],\\"valueWithOccurrence\\":[1,2,3,4],\\"number\\":123,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}", + "headers" : { + "Content-Type" : "application/json" + }, + "transformers" : [ "response-template" ] } } ''' @@ -8610,11 +8820,16 @@ request: url: /users/1 response: status: 200 - --- request: method: POST url: /users/2 +response: + status: 200 +--- +request: + method: POST + url: /users/3 response: status: 200 @@ -8974,7 +9189,7 @@ visible in your Groovy files. The following examples show how to test the depend Gradle -testCompile("com.example:beer-common:0.0.1-SNAPSHOT") +testCompile("com.example:beer-common:0.0.1.BUILD-SNAPSHOT")
    @@ -9013,7 +9228,7 @@ following example: Gradle -classpath "com.example:beer-common:0.0.1-SNAPSHOT" +classpath "com.example:beer-common:0.0.1.BUILD-SNAPSHOT"
    @@ -9062,6 +9277,9 @@ then: } } } + +You can set the Spring Cloud Contract plugin up by setting convertToYaml to true. That way you will NOT have to add the dependency with the extended functionality to the consumer side, since the consumer side will be using YAML contracts instead of Groovy ones. +