1778 lines
64 KiB
Plaintext
1778 lines
64 KiB
Plaintext
== Contract DSL
|
||
|
||
Spring Cloud Contract supports out of the box 2 types of DSL. One written in
|
||
`Groovy` and one written in `YAML`.
|
||
|
||
If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy
|
||
before. Knowledge of the language is not really needed, as the Contract DSL uses only a
|
||
tiny subset of it (only literals, method calls and closures). Also, the DSL is statically
|
||
typed, to make it programmer-readable without any knowledge of the DSL itself.
|
||
|
||
IMPORTANT: Remember that, inside the Groovy contract file, you have to provide the fully
|
||
qualified name to the `Contract` class and `make` static imports, such as
|
||
`org.springframework.cloud.spec.Contract.make { ... }`. You can also provide an import to
|
||
the `Contract` class: `import org.springframework.cloud.spec.Contract` and then call
|
||
`Contract.make { ... }`.
|
||
|
||
TIP: Spring Cloud Contract supports defining multiple contracts in a single file.
|
||
|
||
The following is a complete example of a Groovy contract definition:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=dsl_example,indent=0]
|
||
----
|
||
|
||
The following is a complete example of a YAML contract definition:
|
||
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0]
|
||
----
|
||
|
||
TIP: You can compile contracts to stubs mapping using standalone maven command:
|
||
`mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert`
|
||
|
||
=== Limitations
|
||
|
||
WARNING: Spring Cloud Contract Verifier does not properly support XML. Please use JSON or
|
||
help us implement this feature.
|
||
|
||
WARNING: The support for verifying the size of JSON arrays is experimental. If you want
|
||
to turn it on, please set the value of the following system property to `true`:
|
||
`spring.cloud.contract.verifier.assert.size`. By default, this feature is set to `false`.
|
||
You can also provide the `assertJsonSize` property in the plugin configuration.
|
||
|
||
WARNING: Because JSON structure can have any form, it can be impossible to parse it
|
||
properly when using the Groovy DSL and the `value(consumer(...), producer(...))` notation in `GString`. That
|
||
is why you should use the Groovy Map notation.
|
||
|
||
=== Common Top-Level elements
|
||
|
||
The following sections describe the most common top-level elements:
|
||
|
||
* <<contract-dsl-description>>
|
||
* <<contract-dsl-name>>
|
||
* <<contract-dsl-ignoring-contracts>>
|
||
* <<contract-dsl-passing-values-from-files>>
|
||
* <<contract-dsl-http-top-level-elements>>
|
||
|
||
[[contract-dsl-description]]
|
||
==== Description
|
||
|
||
You can add a `description` to your contract. The description is arbitrary text. The
|
||
following code shows an example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=description,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0]
|
||
----
|
||
|
||
[[contract-dsl-name]]
|
||
==== Name
|
||
|
||
You can provide a name for your contract. Assume that you provided the following name:
|
||
`should register a user`. If you do so, the name of the autogenerated test is
|
||
`validate_should_register_a_user`. Also, the name of the stub in a WireMock stub is
|
||
`should_register_a_user.json`.
|
||
|
||
IMPORTANT: You must ensure that the name does not contain any characters that make the
|
||
generated test not compile. Also, remember that, if you provide the same name for
|
||
multiple contracts, your autogenerated tests fail to compile and your generated stubs
|
||
override each other.
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=name,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=name,indent=0]
|
||
----
|
||
|
||
[[contract-dsl-ignoring-contracts]]
|
||
==== Ignoring Contracts
|
||
|
||
If you want to ignore a contract, you can either set a value of ignored contracts in the
|
||
plugin configuration or set the `ignored` property on the contract itself:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=ignored,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=ignored,indent=0]
|
||
----
|
||
|
||
[[contract-dsl-passing-values-from-files]]
|
||
==== Passing Values from Files
|
||
|
||
Starting with version `1.2.0`, you can pass values from files. Assume that you have the
|
||
following resources in our project.
|
||
|
||
[source,bash,indent=0]
|
||
----
|
||
└── src
|
||
└── test
|
||
└── resources
|
||
└── contracts
|
||
├── readFromFile.groovy
|
||
├── request.json
|
||
└── response.json
|
||
----
|
||
|
||
Further assume that your contract is as follows:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/classpath/readFromFile.groovy[indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_from_file.yml[indent=0]
|
||
----
|
||
|
||
Further assume that the JSON files is as follows:
|
||
|
||
*request.json*
|
||
[source,json,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/classpath/request.json[indent=0]
|
||
----
|
||
|
||
*response.json*
|
||
[source,json,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/classpath/response.json[indent=0]
|
||
----
|
||
|
||
When test or stub generation takes place, the contents of the file is passed to the body
|
||
of a request or a response. The name of the file needs to be a file with location
|
||
relative to the folder in which the contract lays.
|
||
|
||
[[contract-dsl-http-top-level-elements]]
|
||
==== HTTP Top-Level Elements
|
||
|
||
The following methods can be called in the top-level closure of a contract definition.
|
||
`request` and `response` are mandatory. `priority` is optional.
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=http_dsl,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=priority,indent=0]
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
|
||
...
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0]
|
||
...
|
||
----
|
||
|
||
IMPORTANT: If you want to make your contract have a **higher** value of priority
|
||
you need to pass a **lower** number to the `priority` tag / method. E.g. `priority` with
|
||
value `5` has **higher** priority than `priority` with value `10`.
|
||
|
||
=== Request
|
||
|
||
The HTTP protocol requires only **method and url** to be specified in a request. The
|
||
same information is mandatory in request definition of the Contract.
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=request,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request_obligatory,indent=0]
|
||
----
|
||
|
||
It is possible to specify an absolute rather than relative `url`, but using `urlPath` is
|
||
the recommended way, as doing so makes the tests **host-independent**.
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=url,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_rest_with_path.yml[tags=url_path,indent=0]
|
||
----
|
||
|
||
`request` may contain **query parameters**.
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=urlpath,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
|
||
...
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=query_params,indent=0]
|
||
----
|
||
|
||
`request` may contain additional **request headers**, as shown in the following example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=headers,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
|
||
...
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=headers,indent=0]
|
||
----
|
||
|
||
`request` may contain a **request body**:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=body,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0]
|
||
...
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=body,indent=0]
|
||
----
|
||
|
||
`request` may contain **multipart** elements. To include multipart elements, use the
|
||
`multipart` method/section, as shown in the following examples
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=multipartdsl,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_multipart.yml[indent=0]
|
||
----
|
||
|
||
In the preceding example, we define parameters in either of two ways:
|
||
|
||
.Groovy DSL
|
||
* Directly, by using the map notation, where the value can be a dynamic property (such as
|
||
`formParameter: $(consumer(...), producer(...))`).
|
||
* By using the `named(...)` method that lets you set a named parameter. A named parameter
|
||
can set a `name` and `content`. You can call it either via a method with two arguments,
|
||
such as `named("fileName", "fileContent")`, or via a map notation, such as
|
||
`named(name: "fileName", content: "fileContent")`.
|
||
|
||
.YAML
|
||
* The multipart parameters are set via `multipart.params` section
|
||
* The named parameters (the `fileName` and `fileContent` for a given parameter name)
|
||
can be set via the `multipart.named` section. That section contains
|
||
the `paramName` (name of the parameter), `fileName` (name of the file),
|
||
`fileContent` (content of the file) fields
|
||
* The dynamic bits can be set via the `matchers.multipart` section
|
||
** for parameters use the `params` section that can accept
|
||
`regex` or a `predefined` regular expression
|
||
** for named params use the `named` section where first you
|
||
define the parameter name via `paramName` and then you can pass the
|
||
parametrization of either `fileName` or `fileContent` via
|
||
`regex` or a `predefined` regular expression
|
||
|
||
From this contract, the generated test is as follows:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "multipart/form-data;boundary=AaB03x")
|
||
.param("formParameter", "\"formParameterValue\"")
|
||
.param("someBooleanParameter", "true")
|
||
.multiPart("file", "filename.csv", "file content".getBytes());
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.put("/multipart");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
----
|
||
|
||
The WireMock stub is as follows:
|
||
|
||
[source,json,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy[tags=multipartwiremock,indent=0]
|
||
----
|
||
|
||
=== Response
|
||
|
||
The response must contain an **HTTP status code** and may contain other information. The
|
||
following code shows an example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=response,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0]
|
||
...
|
||
include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response_obligatory,indent=0]
|
||
----
|
||
|
||
Besides status, the response may contain **headers** and a **body**, both of which are
|
||
specified the same way as in the request (see the previous paragraph).
|
||
|
||
TIP: Via the Groovy DSL you can reference the `org.springframework.cloud.contract.spec.internal.HttpStatus`
|
||
methods to provide a meaningful status instead of a digit. E.g. you can call
|
||
`OK()` for a status `200` or `BAD_REQUEST()` for `400`.
|
||
|
||
=== Dynamic properties
|
||
|
||
The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
|
||
want to force the consumers to stub their clocks to always return the same value of time
|
||
so that it gets matched by the stub.
|
||
|
||
For Groovy DSL you can provide the dynamic parts in your contracts
|
||
in two ways: pass them directly in the body or set them in a separate section called
|
||
`bodyMatchers`.
|
||
|
||
NOTE: Before 2.0.0 these were set using `testMatchers` and `stubMatchers`,
|
||
check out the https://github.com/spring-cloud/spring-cloud-contract/wiki/Spring-Cloud-Contract-2.0-Migration-Guide[migration guide] for more information.
|
||
|
||
For YAML you can only use the `matchers` section.
|
||
|
||
==== Dynamic properties inside the body
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL. Check out the
|
||
<<contract-matchers>> section for YAML examples of a similar feature.
|
||
|
||
You can set the properties inside the body either with the `value` method or, if you use
|
||
the Groovy map notation, with `$()`. The following example shows how to set dynamic
|
||
properties with the value method:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
value(consumer(...), producer(...))
|
||
value(c(...), p(...))
|
||
value(stub(...), test(...))
|
||
value(client(...), server(...))
|
||
----
|
||
|
||
The following example shows how to set dynamic properties with `$()`:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
$(consumer(...), producer(...))
|
||
$(c(...), p(...))
|
||
$(stub(...), test(...))
|
||
$(client(...), server(...))
|
||
----
|
||
|
||
Both approaches work equally well. `stub` and `client` methods are aliases over the `consumer`
|
||
method. Subsequent sections take a closer look at what you can do with those values.
|
||
|
||
==== Regular expressions
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL. Check out the
|
||
<<contract-matchers>> section for YAML examples of a similar feature.
|
||
|
||
You can use regular expressions to write your requests in Contract DSL. Doing so is
|
||
particularly useful when you want to indicate that a given response should be provided
|
||
for requests that follow a given pattern. Also, you can use regular expressions when you
|
||
need to use patterns and not exact values both for your test and your server side tests.
|
||
|
||
The following example shows how to use regular expressions to write a request:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=regex,indent=0]
|
||
----
|
||
|
||
You can also provide only one side of the communication with a regular expression. If you
|
||
do so, then the contract engine automatically provides the generated string that matches
|
||
the provided regular expression. The following code shows an example:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=dsl_one_side_data_generation_example,indent=0]
|
||
----
|
||
|
||
In the preceding example, the opposite side of the communication has the respective data
|
||
generated for request and response.
|
||
|
||
Spring Cloud Contract comes with a series of predefined regular expressions that you can
|
||
use in your contracts, as shown in the following example:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{contract_spec_path}/src/main/groovy/org/springframework/cloud/contract/spec/internal/RegexPatterns.groovy[tags=regexps,indent=0]
|
||
----
|
||
|
||
In your contract, you can use it as shown in the following example:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=contract_with_regex,indent=0]
|
||
----
|
||
|
||
==== Passing Optional Parameters
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL. Check out the
|
||
<<contract-matchers>> section for YAML examples of a similar feature.
|
||
|
||
It is possible to provide optional parameters in your contract. However, you can provide
|
||
optional parameters only for the following:
|
||
|
||
* __STUB__ side of the Request
|
||
* __TEST__ side of the Response
|
||
|
||
The following example shows how to provide optional parameters:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=optionals,indent=0]
|
||
----
|
||
|
||
By wrapping a part of the body with the `optional()` method, you create a regular
|
||
expression that must be present 0 or more times.
|
||
|
||
If you use Spock for, the following test would be generated from the previous example:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=optionals_test,indent=0]
|
||
----
|
||
|
||
The following stub would also be generated:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{plugins_path}/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy[tags=wiremock,indent=0]
|
||
----
|
||
|
||
==== Executing Custom Methods on the Server Side
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL. Check out the
|
||
<<contract-matchers>> section for YAML examples of a similar feature.
|
||
|
||
You can define a method call that executes on the server side during the test. Such a
|
||
method can be added to the class defined as "baseClassForTests" in the configuration. The
|
||
following code shows an example of the contract portion of the test case:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=method,indent=0]
|
||
----
|
||
|
||
The following code shows the base class portion of the test case:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/src/test/groovy/org/springframework/cloud/contract/verifier/twitter/places/BaseMockMvcSpec.groovy[tags=base_class,indent=0]
|
||
----
|
||
|
||
IMPORTANT: You cannot use both a String and `execute` to perform concatenation. For
|
||
example, calling `header('Authorization', 'Bearer ' + execute('authToken()'))` leads to
|
||
improper results. Instead, call `header('Authorization', execute('authToken()'))` and
|
||
ensure that the `authToken()` method returns everything you need.
|
||
|
||
The type of the object read from the JSON can be one of the following, depending on the
|
||
JSON path:
|
||
|
||
* `String`: If you point to a `String` value in the JSON.
|
||
* `JSONArray`: If you point to a `List` in the JSON.
|
||
* `Map`: If you point to a `Map` in the JSON.
|
||
* `Number`: If you point to `Integer`, `Double` etc. in the JSON.
|
||
* `Boolean`: If you point to a `Boolean` in the JSON.
|
||
|
||
In the request part of the contract, you can specify that the `body` should be taken from
|
||
a method.
|
||
|
||
IMPORTANT: You must provide both the consumer and the producer side. The `execute` part
|
||
is applied for the whole body - not for parts of it.
|
||
|
||
The following example shows how to read an object from JSON:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy[tags=body_execute,indent=0]
|
||
----
|
||
|
||
The preceding example results in calling the `hashCode()` method in the request body.
|
||
It should resemble the following code:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.body(hashCode());
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.get("/something");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
----
|
||
|
||
==== Referencing the Request from the Response
|
||
|
||
The best situation is to provide fixed values, but sometimes you need to reference a
|
||
request in your response.
|
||
|
||
If you're writing contracts using Groovy DSL, you can use the `fromRequest()` method, which lets
|
||
you reference a bunch of elements from the HTTP request. You can use the following
|
||
options:
|
||
|
||
* `fromRequest().url()`: Returns the request URL and query parameters.
|
||
* `fromRequest().query(String key)`: Returns the first query parameter with a given name.
|
||
* `fromRequest().query(String key, int index)`: Returns the nth query parameter with a
|
||
given name.
|
||
* `fromRequest().path()`: Returns the full path.
|
||
* `fromRequest().path(int index)`: Returns the nth path element.
|
||
* `fromRequest().header(String key)`: Returns the first header with a given name.
|
||
* `fromRequest().header(String key, int index)`: Returns the nth header with a given name.
|
||
* `fromRequest().body()`: Returns the full request body.
|
||
* `fromRequest().body(String jsonPath)`: Returns the element from the request that
|
||
matches the JSON Path.
|
||
|
||
If you're using the YAML contract definition you have to use the
|
||
http://handlebarsjs.com/[Handlebars] `{{{ }}}` notation with custom, Spring Cloud Contract
|
||
functions to achieve this.
|
||
|
||
* `{{{ request.url }}}`: Returns the request URL and query parameters.
|
||
* `{{{ request.query.key.[index] }}}`: Returns the nth query parameter with a given name.
|
||
E.g. for key `foo`, first entry `{{{ request.query.foo.[0] }}}`
|
||
* `{{{ request.path }}}`: Returns the full path.
|
||
* `{{{ request.path.[index] }}}`: Returns the nth path element. E.g.
|
||
for first entry ```{{{ request.path.[0] }}}
|
||
* `{{{ request.headers.key }}}`: Returns the first header with a given name.
|
||
* `{{{ request.headers.key.[index] }}}`: Returns the nth header with a given name.
|
||
* `{{{ request.body }}}`: Returns the full request body.
|
||
* `{{{ jsonpath this 'your.json.path' }}}`: Returns the element from the request that
|
||
matches the JSON Path. E.g. for json path `$.foo` - `{{{ jsonpath this '$.foo' }}}`
|
||
|
||
Consider the following contract:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=template_contract,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_reference_request.yml[indent=0]
|
||
----
|
||
|
||
Running a JUnit test generation leads to a test that resembles the following example:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Authorization", "secret")
|
||
.header("Authorization", "secret2")
|
||
.body("{\"foo\":\"bar\",\"baz\":5}");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.queryParam("foo","bar")
|
||
.queryParam("foo","bar2")
|
||
.get("/api/v1/xxxx");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
assertThat(response.header("Authorization")).isEqualTo("foo secret bar");
|
||
// and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||
assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\"foo\":\"bar\",\"baz\":5}");
|
||
assertThatJson(parsedJson).field("['authorization']").isEqualTo("secret");
|
||
assertThatJson(parsedJson).field("['authorization2']").isEqualTo("secret2");
|
||
assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx");
|
||
assertThatJson(parsedJson).field("['param']").isEqualTo("bar");
|
||
assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2");
|
||
assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1");
|
||
assertThatJson(parsedJson).field("['responseBaz']").isEqualTo(5);
|
||
assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar");
|
||
assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2");
|
||
assertThatJson(parsedJson).field("['responseBaz2']").isEqualTo("Bla bla bar bla bla");
|
||
----
|
||
|
||
As you can see, elements from the request have been properly referenced in the response.
|
||
|
||
The generated WireMock stub should resemble the following example:
|
||
|
||
[source,json,indent=0]
|
||
----
|
||
{
|
||
"request" : {
|
||
"urlPath" : "/api/v1/xxxx",
|
||
"method" : "POST",
|
||
"headers" : {
|
||
"Authorization" : {
|
||
"equalTo" : "secret2"
|
||
}
|
||
},
|
||
"queryParameters" : {
|
||
"foo" : {
|
||
"equalTo" : "bar2"
|
||
}
|
||
},
|
||
"bodyPatterns" : [ {
|
||
"matchesJsonPath" : "$[?(@.['baz'] == 5)]"
|
||
}, {
|
||
"matchesJsonPath" : "$[?(@.['foo'] == 'bar')]"
|
||
} ]
|
||
},
|
||
"response" : {
|
||
"status" : 200,
|
||
"body" : "{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}",
|
||
"headers" : {
|
||
"Authorization" : "{{{request.headers.Authorization.[0]}}};foo"
|
||
},
|
||
"transformers" : [ "response-template" ]
|
||
}
|
||
}
|
||
----
|
||
|
||
Sending a request such as the one presented in the `request` part of the contract results
|
||
in sending the following response body:
|
||
|
||
[source,json,indent=0]
|
||
----
|
||
{
|
||
"url" : "/api/v1/xxxx?foo=bar&foo=bar2",
|
||
"path" : "/api/v1/xxxx",
|
||
"pathIndex" : "v1",
|
||
"param" : "bar",
|
||
"paramIndex" : "bar2",
|
||
"authorization" : "secret",
|
||
"authorization2" : "secret2",
|
||
"fullBody" : "{\"foo\":\"bar\",\"baz\":5}",
|
||
"responseFoo" : "bar",
|
||
"responseBaz" : 5,
|
||
"responseBaz2" : "Bla bla bar bla bla"
|
||
}
|
||
----
|
||
|
||
IMPORTANT: This feature works only with WireMock having a version greater than or equal
|
||
to 2.5.1. The Spring Cloud Contract Verifier uses WireMock's
|
||
`response-template` response transformer. It uses Handlebars to convert the Mustache `{{{ }}}` templates into
|
||
proper values. Additionally, it registers two helper functions:
|
||
|
||
* `escapejsonbody`: Escapes the request body in a format that can be embedded in a JSON.
|
||
* `jsonpath`: For a given parameter, find an object in the request body.
|
||
|
||
==== Registering Your Own WireMock Extension
|
||
|
||
WireMock lets you register custom extensions. By default, Spring Cloud Contract registers
|
||
the transformer, which lets you reference a request from a response. If you want to
|
||
provide your own extensions, you can register an implementation of the
|
||
`org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions` interface.
|
||
Since we use the spring.factories extension approach, you can create an entry in
|
||
`META-INF/spring.factories` file similar to the following:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{stubrunner_core_path}/src/test/resources/META-INF/spring.factories[indent=0]
|
||
----
|
||
|
||
The following is an example of a custom extension:
|
||
|
||
.TestWireMockExtensions.groovy
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/TestWireMockExtensions.groovy[indent=0]
|
||
----
|
||
|
||
IMPORTANT: Remember to override the `applyGlobally()` method and set it to `false` if you
|
||
want the transformation to be applied only for a mapping that explicitly requires it.
|
||
|
||
[[contract-matchers]]
|
||
==== Dynamic Properties in the Matchers Sections
|
||
|
||
If you work with https://docs.pact.io/[Pact], the following discussion may seem familiar.
|
||
Quite a few users are used to having a separation between the body and setting the
|
||
dynamic parts of a contract.
|
||
|
||
You can use the `bodyMatchers` section for two reasons:
|
||
|
||
* Define the dynamic values that should end up in a stub.
|
||
You can set it in the `request` or `inputMessage` part of your contract.
|
||
* Verify the result of your test.
|
||
This section is present in the `response` or `outputMessage` side of the
|
||
contract.
|
||
|
||
Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
|
||
following matching possibilities:
|
||
|
||
.Groovy DSL
|
||
|
||
* For the stubs:
|
||
** `byEquality()`: The value taken from the response via the provided JSON Path must be
|
||
equal to the value provided in the contract.
|
||
** `byRegex(...)`: The value taken from the response via the provided JSON Path must
|
||
match the regex.
|
||
** `byDate()`: The value taken from the response via the provided JSON Path must
|
||
match the regex for an ISO Date value.
|
||
** `byTimestamp()`: The value taken from the response via the provided JSON Path must
|
||
match the regex for an ISO DateTime value.
|
||
** `byTime()`: The value taken from the response via the provided JSON Path must
|
||
match the regex for an ISO Time value.
|
||
* For the verification:
|
||
** `byEquality()`: The value taken from the response via the provided JSON Path must be
|
||
equal to the provided value in the contract.
|
||
** `byRegex(...)`: The value taken from the response via the provided JSON Path must
|
||
match the regex.
|
||
** `byDate()`: The value taken from the response via the provided JSON Path must match
|
||
the regex for an ISO Date value.
|
||
** `byTimestamp()`: The value taken from the response via the provided JSON Path must
|
||
match the regex for an ISO DateTime value.
|
||
** `byTime()`: The value taken from the response via the provided JSON Path must match
|
||
the regex for an ISO Time value.
|
||
** `byType()`: The value taken from the response via the provided JSON Path needs to be
|
||
of the same type as the type defined in the body of the response in the contract.
|
||
`byType` can take a closure, in which you can set `minOccurrence` and `maxOccurrence`.
|
||
That way, you can assert the size of the flattened collection. To check the size of an
|
||
unflattened collection, use a custom method with the `byCommand(...)` testMatcher.
|
||
** `byCommand(...)`: The value taken from the response via the provided JSON Path is
|
||
passed as an input to the custom method that you provide. For example,
|
||
`byCommand('foo($it)')` results in calling a `foo` method to which the value matching the
|
||
JSON Path gets passed. The type of the object read from the JSON can be one of the
|
||
following, depending on the JSON path:
|
||
*** `String`: If you point to a `String` value.
|
||
*** `JSONArray`: If you point to a `List`.
|
||
*** `Map`: If you point to a `Map`.
|
||
*** `Number`: If you point to `Integer`, `Double`, or other kind of number.
|
||
*** `Boolean`: If you point to a `Boolean`.
|
||
** `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
|
||
|
||
[source,yml,indent=0]
|
||
----
|
||
- path: $.foo
|
||
type: by_regex
|
||
value: bar
|
||
----
|
||
|
||
Or if you want to use one of the predefined regular expressions
|
||
`[only_alpha_unicode, number, any_boolean, ip_address, hostname,
|
||
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]`:
|
||
|
||
[source,yml,indent=0]
|
||
----
|
||
- 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
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy[tags=matchers,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_matchers.yml[indent=0]
|
||
----
|
||
|
||
In the preceding example, you can see the dynamic portions of the contract in the
|
||
`matchers` sections. For the request part, you can see that, for all fields but
|
||
`valueWithoutAMatcher`, the values of the regular expressions that the stub should
|
||
contain are explicitly set. For the `valueWithoutAMatcher`, the verification takes place
|
||
in the same way as without the use of matchers. In that case, the test performs an
|
||
equality check.
|
||
|
||
For the response side in the `bodyMatchers` section, we define the dynamic parts in a
|
||
similar manner. The only difference is that the `byType` matchers are also present. The
|
||
verifier engine checks four fields to verify whether the response from the test
|
||
has a value for which the JSON path matches the given field, is of the same type as the one
|
||
defined in the response body, and passes the following check (based on the method being called):
|
||
|
||
* For `$.valueWithTypeMatch`, the engine checks whether the type is the same.
|
||
* For `$.valueWithMin`, the engine check the type and asserts whether the size is greater
|
||
than or equal to the minimum occurrence.
|
||
* For `$.valueWithMax`, the engine checks the type and asserts whether the size is
|
||
smaller than or equal to the maximum occurrence.
|
||
* For `$.valueWithMinMax`, the engine checks the type and asserts whether the size is
|
||
between the min and maximum occurrence.
|
||
|
||
The resulting test would resemble the following example (note that an `and` section
|
||
separates the autogenerated assertions and the assertion from matchers):
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "application/json")
|
||
.body("{\"duck\":123,\"alpha\":\"abc\",\"number\":123,\"aBoolean\":true,\"date\":\"2017-01-01\",\"dateTime\":\"2017-01-01T01:23:45\",\"time\":\"01:02:34\",\"valueWithoutAMatcher\":\"foo\",\"valueWithTypeMatch\":\"string\",\"key\":{\"complex.key\":\"foo\"}}");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.get("/get");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
assertThat(response.header("Content-Type")).matches("application/json.*");
|
||
// and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||
assertThatJson(parsedJson).field("['valueWithoutAMatcher']").isEqualTo("foo");
|
||
// and:
|
||
assertThat(parsedJson.read("$.duck", String.class)).matches("[0-9]{3}");
|
||
assertThat(parsedJson.read("$.duck", Integer.class)).isEqualTo(123);
|
||
assertThat(parsedJson.read("$.alpha", String.class)).matches("[\\p{L}]*");
|
||
assertThat(parsedJson.read("$.alpha", String.class)).isEqualTo("abc");
|
||
assertThat(parsedJson.read("$.number", String.class)).matches("-?(\\d*\\.\\d+|\\d+)");
|
||
assertThat(parsedJson.read("$.aBoolean", String.class)).matches("(true|false)");
|
||
assertThat(parsedJson.read("$.date", String.class)).matches("(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])");
|
||
assertThat(parsedJson.read("$.dateTime", String.class)).matches("([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
|
||
assertThat(parsedJson.read("$.time", String.class)).matches("(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])");
|
||
assertThat((Object) parsedJson.read("$.valueWithTypeMatch")).isInstanceOf(java.lang.String.class);
|
||
assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMin", java.util.Collection.class)).as("$.valueWithMin").hasSizeGreaterThanOrEqualTo(1);
|
||
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMax", java.util.Collection.class)).as("$.valueWithMax").hasSizeLessThanOrEqualTo(3);
|
||
assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinMax", java.util.Collection.class)).as("$.valueWithMinMax").hasSizeBetween(1, 3);
|
||
assertThat((Object) parsedJson.read("$.valueWithMinEmpty")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMinEmpty", java.util.Collection.class)).as("$.valueWithMinEmpty").hasSizeGreaterThanOrEqualTo(0);
|
||
assertThat((Object) parsedJson.read("$.valueWithMaxEmpty")).isInstanceOf(java.util.List.class);
|
||
assertThat((java.lang.Iterable) parsedJson.read("$.valueWithMaxEmpty", java.util.Collection.class)).as("$.valueWithMaxEmpty").hasSizeLessThanOrEqualTo(0);
|
||
assertThatValueIsANumber(parsedJson.read("$.duck"));
|
||
assertThat(parsedJson.read("$.['key'].['complex.key']", String.class)).isEqualTo("foo");
|
||
----
|
||
|
||
IMPORTANT: Notice that, for the `byCommand` method, the example calls the
|
||
`assertThatValueIsANumber`. This method must be defined in the test base class or be
|
||
statically imported to your tests. Notice that the `byCommand` call was converted to
|
||
`assertThatValueIsANumber(parsedJson.read("$.duck"));`. That means that the engine took
|
||
the method name and passed the proper JSON path as a parameter to it.
|
||
|
||
The resulting WireMock stub is in the following example:
|
||
|
||
[source,json,indent=0]
|
||
----
|
||
include::{plugins_path}/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy[tags=matchers,indent=0]
|
||
----
|
||
|
||
IMPORTANT: If you use a `matcher`, then the part of the request and response that the
|
||
`matcher` addresses with the JSON Path gets removed from the assertion. In the case of
|
||
verifying a collection, you must create matchers for *all* the elements of the
|
||
collection.
|
||
|
||
Consider the following example:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
Contract.make {
|
||
request {
|
||
method 'GET'
|
||
url("/foo")
|
||
}
|
||
response {
|
||
status OK()
|
||
body(events: [[
|
||
operation : 'EXPORT',
|
||
eventId : '16f1ed75-0bcc-4f0d-a04d-3121798faf99',
|
||
status : 'OK'
|
||
], [
|
||
operation : 'INPUT_PROCESSING',
|
||
eventId : '3bb4ac82-6652-462f-b6d1-75e424a0024a',
|
||
status : 'OK'
|
||
]
|
||
]
|
||
)
|
||
bodyMatchers {
|
||
jsonPath('$.events[0].operation', byRegex('.+'))
|
||
jsonPath('$.events[0].eventId', byRegex('^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'))
|
||
jsonPath('$.events[0].status', byRegex('.+'))
|
||
}
|
||
}
|
||
}
|
||
----
|
||
|
||
The preceding code leads to creating the following test (the code block shows only the assertion section):
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
|
||
assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("16f1ed75-0bcc-4f0d-a04d-3121798faf99")
|
||
assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("EXPORT")
|
||
assertThatJson(parsedJson).array("['events']").contains("['operation']").isEqualTo("INPUT_PROCESSING")
|
||
assertThatJson(parsedJson).array("['events']").contains("['eventId']").isEqualTo("3bb4ac82-6652-462f-b6d1-75e424a0024a")
|
||
assertThatJson(parsedJson).array("['events']").contains("['status']").isEqualTo("OK")
|
||
and:
|
||
assertThat(parsedJson.read("\$.events[0].operation", String.class)).matches(".+")
|
||
assertThat(parsedJson.read("\$.events[0].eventId", String.class)).matches("^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\$")
|
||
assertThat(parsedJson.read("\$.events[0].status", String.class)).matches(".+")
|
||
----
|
||
|
||
As you can see, the assertion is malformed. Only the first element of the array got
|
||
asserted. In order to fix this, you should apply the assertion to the whole `$.events`
|
||
collection and assert it with the `byCommand(...)` method.
|
||
|
||
=== JAX-RS Support
|
||
|
||
The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs
|
||
to define `protected WebTarget webTarget` and server initialization. The only option for
|
||
testing JAX-RS API is to start a web server. Also, a request with a body needs to have a
|
||
content type set. Otherwise, the default of `application/octet-stream` gets used.
|
||
|
||
In order to use JAX-RS mode, use the following settings:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
testMode == 'JAXRSCLIENT'
|
||
----
|
||
|
||
The following example shows a generated test API:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/JaxRsClientMethodBuilderSpec.groovy[tags=jaxrs,indent=0]
|
||
----
|
||
|
||
=== Async Support
|
||
|
||
If you're using asynchronous communication on the server side (your controllers are
|
||
returning `Callable`, `DeferredResult`, and so on), then, inside your contract, you must
|
||
provide an `async()` method in the `response` section. The following code shows an example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
org.springframework.cloud.contract.spec.Contract.make {
|
||
request {
|
||
method GET()
|
||
url '/get'
|
||
}
|
||
response {
|
||
status OK()
|
||
body 'Passed'
|
||
async()
|
||
}
|
||
}
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
response:
|
||
async: true
|
||
----
|
||
|
||
=== Working with Context Paths
|
||
|
||
Spring Cloud Contract supports context paths.
|
||
|
||
IMPORTANT: The only change needed to fully support context paths is the switch on the
|
||
*PRODUCER* side. Also, the autogenerated tests must use *EXPLICIT* mode. The consumer
|
||
side remains untouched. In order for the generated test to pass, you must use *EXPLICIT*
|
||
mode.
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
<plugin>
|
||
<groupId>org.springframework.cloud</groupId>
|
||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||
<version>${spring-cloud-contract.version}</version>
|
||
<extensions>true</extensions>
|
||
<configuration>
|
||
<testMode>EXPLICIT</testMode>
|
||
</configuration>
|
||
</plugin>
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
contracts {
|
||
testMode = 'EXPLICIT'
|
||
}
|
||
----
|
||
|
||
That way, you generate a test that *DOES NOT* use MockMvc. It means that you generate
|
||
real requests and you need to setup your generated test's base class to work on a real
|
||
socket.
|
||
|
||
Consider the following contract:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy[tags=context_path_contract,indent=0]
|
||
----
|
||
|
||
The following example shows how to set up a base class and Rest Assured:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGeneratorSpec.groovy[tags=context_path_baseclass,indent=0]
|
||
----
|
||
|
||
If you do it this way:
|
||
|
||
* All of your requests in the autogenerated tests are sent to the real endpoint with your
|
||
context path included (for example, `/my-context-path/url`).
|
||
* Your contracts reflect that you have a context path. Your generated stubs also have
|
||
that information (for example, in the stubs, you have to call `/my-context-path/url`).
|
||
|
||
=== Messaging Top-Level Elements
|
||
|
||
The DSL for messaging looks a little bit different than the one that focuses on HTTP. The
|
||
following sections explain the differences:
|
||
|
||
* <<contract-dsl-output-triggered-method>>
|
||
* <<contract-dsl-output-triggered-message>>
|
||
* <<contract-dsl-consumer-producer>>
|
||
* <<contract-dsl-common>>
|
||
|
||
[[contract-dsl-output-triggered-method]]
|
||
==== Output Triggered by a Method
|
||
|
||
The output message can be triggered by calling a method (such as a `Scheduler` when a was
|
||
started and a message was sent), as shown in the following example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy]
|
||
----
|
||
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=method_trigger,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_message_method.yml[indent=0]
|
||
----
|
||
|
||
In the previous example case, the output message is sent to `output` if a method called
|
||
`bookReturnedTriggered` is executed. On the message *publisher's* side, we generate a
|
||
test that calls that method to trigger the message. On the *consumer* side, you can use
|
||
the `some_label` to trigger the message.
|
||
|
||
[[contract-dsl-output-triggered-message]]
|
||
==== Output Triggered by a Message
|
||
|
||
The output message can be triggered by receiving a message, as shown in the following
|
||
example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy]
|
||
----
|
||
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=message_trigger,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/contract_message_input_message.yml[indent=0]
|
||
----
|
||
|
||
In the preceding example, the output message is sent to `output` if a proper message is
|
||
received on the `input` destination. On the message *publisher's* side, the engine
|
||
generates a test that sends the input message to the defined destination. On the
|
||
*consumer* side, you can either send a message to the input destination or use a label
|
||
(`some_label` in the example) to trigger the message.
|
||
|
||
[[contract-dsl-consumer-producer]]
|
||
==== Consumer/Producer
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL.
|
||
|
||
In HTTP, you have a notion of `client`/`stub and `server`/`test` notation. You can also
|
||
use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also
|
||
provides the `consumer` and `producer` methods, as presented in the following example
|
||
(note that you can use either `$` or `value` methods to provide `consumer` and `producer`
|
||
parts):
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=consumer_producer]
|
||
----
|
||
|
||
[[contract-dsl-common]]
|
||
==== Common
|
||
|
||
In the `input` or `outputMessage` section you can call `assertThat` with the name
|
||
of a `method` (e.g. `assertThatMessageIsOnTheQueue()`) that you have defined in the
|
||
base class or in a static import. Spring Cloud Contract will execute that method
|
||
in the generated test.
|
||
|
||
=== Multiple Contracts in One File
|
||
|
||
You can define multiple contracts in one file. Such a contract might resemble the
|
||
following example:
|
||
|
||
.Groovy DSL
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/multiple-contracts/src/test/resources/contracts/com/hello/v1/WithList.groovy[lines=18..-1,indent=0]
|
||
----
|
||
|
||
.YAML
|
||
[source,yml,indent=0]
|
||
----
|
||
include::{verifier_core_path}/src/test/resources/yml/multiple_contracts.yml[indent=0]
|
||
----
|
||
|
||
In the preceding example, one contract has the `name` field and the other does not. This
|
||
leads to generation of two tests that look more or less like this:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
package org.springframework.cloud.contract.verifier.tests.com.hello;
|
||
|
||
import com.example.TestBase;
|
||
import com.jayway.jsonpath.DocumentContext;
|
||
import com.jayway.jsonpath.JsonPath;
|
||
import com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
|
||
import com.jayway.restassured.response.ResponseOptions;
|
||
import org.junit.Test;
|
||
|
||
import static com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;
|
||
import static com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
|
||
import static org.assertj.core.api.Assertions.assertThat;
|
||
|
||
public class V1Test extends TestBase {
|
||
|
||
@Test
|
||
public void validate_should_post_a_user() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given();
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.post("/users/1");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
}
|
||
|
||
@Test
|
||
public void validate_withList_1() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given();
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.post("/users/2");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
}
|
||
|
||
}
|
||
----
|
||
|
||
Notice that, for the contract that has the `name` field, the generated test method is named
|
||
`validate_should_post_a_user`. For the one that does not have the name, it is called
|
||
`validate_withList_1`. It corresponds to the name of the file `WithList.groovy` and the
|
||
index of the contract in the list.
|
||
|
||
The generated stubs is shown in the following example:
|
||
|
||
[source]
|
||
----
|
||
should post a user.json
|
||
1_WithList.json
|
||
----
|
||
|
||
As you can see, the first file got the `name` parameter from the contract. The second
|
||
got the name of the contract file (`WithList.groovy`) prefixed with the index (in this
|
||
case, the contract had an index of `1` in the list of contracts in the file).
|
||
|
||
TIP: As you can see, it iss much better if you name your contracts because doing so makes
|
||
your tests far more meaningful.
|
||
|
||
== Customization
|
||
|
||
IMPORTANT: This section is valid only for Groovy DSL
|
||
|
||
You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in
|
||
the remainder of this section.
|
||
|
||
=== Extending the DSL
|
||
|
||
You can provide your own functions to the DSL. The key requirement for this feature is to
|
||
maintain the static compatibility. Later in this document, you can see examples of:
|
||
|
||
* Creating a JAR with reusable classes.
|
||
* Referencing of these classes in the DSLs.
|
||
|
||
You can find the full example
|
||
https://github.com/spring-cloud-samples/spring-cloud-contract-samples[here].
|
||
|
||
==== Common JAR
|
||
|
||
The following examples show three classes that can be reused in the DSLs.
|
||
|
||
*PatternUtils* contains functions used by both the **consumer** and the **producer**.
|
||
|
||
[source,java]
|
||
----
|
||
include::{samples_url}/common/src/main/java/com/example/PatternUtils.java[]
|
||
----
|
||
|
||
*ConsumerUtils* contains functions used by the **consumer**.
|
||
|
||
[source,java]
|
||
----
|
||
include::{samples_url}/common/src/main/java/com/example/ConsumerUtils.java[]
|
||
----
|
||
|
||
*ProducerUtils* contains functions used by the **producer**.
|
||
|
||
[source,java]
|
||
----
|
||
include::{samples_url}/common/src/main/java/com/example/ProducerUtils.java[]
|
||
----
|
||
|
||
==== Adding the Dependency to the Project
|
||
|
||
In order for the plugins and IDE to be able to reference the common JAR classes, you need
|
||
to pass the dependency to your project.
|
||
|
||
// TODO missing code block here - we should show an example adding a dependency
|
||
|
||
==== Test the Dependency in the Project's Dependencies
|
||
|
||
First, add the common jar dependency as a test dependency. Because your contracts files
|
||
are available on the test resources path, the common jar classes automatically become
|
||
visible in your Groovy files. The following examples show how to test the dependency:
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
include::{samples_url}/producer/pom.xml[tags=test_dep,indent=0]
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
include::{samples_url}/producer/build.gradle[tags=test_dep,indent=0]
|
||
----
|
||
|
||
==== 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:
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
include::{samples_url}/producer/pom.xml[tags=test_dep_in_plugin,indent=0]
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
include::{samples_url}/producer/build.gradle[tags=test_dep_in_plugin,indent=0]
|
||
----
|
||
|
||
==== Referencing classes in DSLs
|
||
|
||
You can now reference your classes in your DSL, as shown in the following example:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{samples_url}/producer/src/test/resources/contracts/beer/rest/shouldGrantABeerIfOldEnough.groovy[indent=0]
|
||
----
|
||
|
||
== Using the Pluggable Architecture
|
||
|
||
You may encounter cases where you have your contracts have been defined in other formats,
|
||
such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic
|
||
generation of tests and stubs. You can add your own implementation for generating both
|
||
tests and stubs. Also, you can customize the way tests are generated (for example, you
|
||
can generate tests for other languages) and the way stubs are generated (for example, you
|
||
can generate stubs for other HTTP server implementations).
|
||
|
||
=== Custom Contract Converter
|
||
|
||
The `ContractConverter` interface lets you register your own implementation of a contract
|
||
structure converter. The following code listing shows the `ContractConverter` interface:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{contract_spec_path}/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.groovy[indent=0,lines=17..-1]
|
||
----
|
||
|
||
Your implementation must define the condition on which it should start the
|
||
conversion. Also, you must define how to perform that conversion in both directions.
|
||
|
||
IMPORTANT: Once you create your implementation, you must create a
|
||
`/META-INF/spring.factories` file in which you provide the fully qualified name of your
|
||
implementation.
|
||
|
||
The following example shows a typical `spring.factories` file:
|
||
|
||
[source]
|
||
----
|
||
org.springframework.cloud.contract.spec.ContractConverter=\
|
||
org.springframework.cloud.contract.verifier.converter.YamlContractConverter
|
||
----
|
||
|
||
[[pact-converter]]
|
||
==== Pact Converter
|
||
|
||
Spring Cloud Contract includes support for https://docs.pact.io/[Pact] representation of
|
||
contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
|
||
present how to add Pact support for your project. Note however that not all functionality is supported.
|
||
Starting with v3 you can combine multiple matcher for the same element;
|
||
you can use matchers for the body, headers, request and path; and you can use value generators.
|
||
Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
|
||
Next to that the request and path matchers are skipped during the conversion.
|
||
When using a date, time or datetime value generator with a given format,
|
||
the given format will be skipped and the ISO format will be used.
|
||
|
||
|
||
==== Pact Contract
|
||
|
||
Consider following example of a Pact contract, which is a file under the
|
||
`src/test/resources/contracts` folder.
|
||
|
||
[source,javascript,indent=0]
|
||
----
|
||
include::{standalone_pact_path}/pact-http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.json[indent=0]
|
||
----
|
||
|
||
The remainder of this section about using Pact refers to the preceding file.
|
||
|
||
==== Pact for Producers
|
||
|
||
On the producer side, you must add two additional dependencies to your plugin
|
||
configuration. One is the Spring Cloud Contract Pact support, and the other represents
|
||
the current Pact version that you use.
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
include::{standalone_pact_path}/pact-http-server/pom.xml[tags=pact_dependency,indent=0]
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
include::{standalone_pact_path}/pact-http-server/build.gradle[tags=pact_dependency,indent=0]
|
||
----
|
||
|
||
When you execute the build of your application, a test will be generated. The generated
|
||
test might be as follows:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
@Test
|
||
public void validate_shouldMarkClientAsFraud() throws Exception {
|
||
// given:
|
||
MockMvcRequestSpecification request = given()
|
||
.header("Content-Type", "application/vnd.fraud.v1+json")
|
||
.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
|
||
|
||
// when:
|
||
ResponseOptions response = given().spec(request)
|
||
.put("/fraudcheck");
|
||
|
||
// then:
|
||
assertThat(response.statusCode()).isEqualTo(200);
|
||
assertThat(response.header("Content-Type")).matches("application/vnd\\.fraud\\.v1\\+json.*");
|
||
// and:
|
||
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
|
||
assertThatJson(parsedJson).field("['rejectionReason']").isEqualTo("Amount too high");
|
||
// and:
|
||
assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
|
||
}
|
||
----
|
||
|
||
The corresponding generated stub might be as follows:
|
||
|
||
[source,javascript,indent=0]
|
||
----
|
||
{
|
||
"id" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
|
||
"uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
|
||
"request" : {
|
||
"url" : "/fraudcheck",
|
||
"method" : "PUT",
|
||
"headers" : {
|
||
"Content-Type" : {
|
||
"matches" : "application/vnd\\.fraud\\.v1\\+json.*"
|
||
}
|
||
},
|
||
"bodyPatterns" : [ {
|
||
"matchesJsonPath" : "$[?(@.['loanAmount'] == 99999)]"
|
||
}, {
|
||
"matchesJsonPath" : "$[?(@.clientId =~ /([0-9]{10})/)]"
|
||
} ]
|
||
},
|
||
"response" : {
|
||
"status" : 200,
|
||
"body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
|
||
"headers" : {
|
||
"Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
|
||
},
|
||
"transformers" : [ "response-template" ]
|
||
},
|
||
}
|
||
----
|
||
|
||
==== Pact for Consumers
|
||
|
||
On the producer side, you must add two additional dependencies to your project
|
||
dependencies. One is the Spring Cloud Contract Pact support, and the other represents the
|
||
current Pact version that you use.
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
include::{standalone_pact_path}/pact-http-client/pom.xml[tags=pact_dependency,indent=0]
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
include::{standalone_pact_path}/pact-http-client/build.gradle[tags=pact_dependency,indent=0]
|
||
----
|
||
|
||
=== Using the Custom Test Generator
|
||
|
||
If you want to generate tests for languages other than Java or you are not happy with the
|
||
way the verifier builds Java tests, you can register your own implementation.
|
||
|
||
The `SingleTestGenerator` interface lets you register your own implementation. The
|
||
following code listing shows the `SingleTestGenerator` interface:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{verifier_core_path}/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy[indent=0,lines=17..-1]
|
||
----
|
||
|
||
Again, you must provide a `spring.factories` file, such as the one shown in the following
|
||
example:
|
||
|
||
[source]
|
||
----
|
||
org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
|
||
com.example.MyGenerator
|
||
----
|
||
|
||
=== Using the Custom Stub Generator
|
||
|
||
If you want to generate stubs for stub servers other than WireMock, you can plug in your
|
||
own implementation of the `StubGenerator` interface. The following code listing shows the
|
||
`StubGenerator` interface:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{converters_path}/src/main/groovy/org/springframework/cloud/contract/verifier/converter/StubGenerator.groovy[indent=0,lines=16..-1]
|
||
----
|
||
|
||
Again, you must provide a `spring.factories` file, such as the one shown in the following
|
||
example:
|
||
|
||
[source]
|
||
----
|
||
include::{converters_path}/src/main/resources/META-INF/spring.factories[indent=0]
|
||
----
|
||
|
||
The default implementation is the WireMock stub generation.
|
||
|
||
TIP: You can provide multiple stub generator implementations. For example, from a single
|
||
DSL, you can produce both WireMock stubs and Pact files.
|
||
|
||
=== Using the Custom Stub Runner
|
||
|
||
If you decide to use a custom stub generation, you also need a custom way of running
|
||
stubs with your different stub provider.
|
||
|
||
Assume that you use https://github.com/dreamhead/moco[Moco] to build your stubs and that
|
||
you have written a stub generator and placed your stubs in a JAR file.
|
||
|
||
In order for Stub Runner to know how to run your stubs, you have to define a custom
|
||
HTTP Stub server implementation, which might resemble the following example:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::{tests_path}/spring-cloud-contract-stub-runner-moco/src/test/groovy/org/springframework/cloud/contract/stubrunner/provider/moco/MocoHttpServerStub.groovy[indent=0,lines=16..-1]
|
||
----
|
||
|
||
Then, you can register it in your `spring.factories` file, as shown in the following
|
||
example:
|
||
|
||
[source]
|
||
----
|
||
org.springframework.cloud.contract.stubrunner.HttpServerStub=\
|
||
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub
|
||
----
|
||
|
||
Now you can run stubs with Moco.
|
||
|
||
IMPORTANT: If you do not provide any implementation, then the default (WireMock)
|
||
implementation is used. If you provide more than one, the first one on the list is used.
|
||
|
||
=== Using the Custom Stub Downloader
|
||
|
||
You can customize the way your stubs are downloaded by creating an implementation of the
|
||
`StubDownloaderBuilder` interface, as shown in the following example:
|
||
|
||
[source,java]
|
||
----
|
||
package com.example;
|
||
|
||
class CustomStubDownloaderBuilder implements StubDownloaderBuilder {
|
||
|
||
@Override
|
||
public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
|
||
return new StubDownloader() {
|
||
@Override
|
||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||
StubConfiguration config) {
|
||
File unpackedStubs = retrieveStubs();
|
||
return new AbstractMap.SimpleEntry<>(
|
||
new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
|
||
config.getClassifier()), unpackedStubs);
|
||
}
|
||
|
||
File retrieveStubs() {
|
||
// here goes your custom logic to provide a folder where all the stubs reside
|
||
}
|
||
}
|
||
----
|
||
|
||
Then you can register it in your `spring.factories` file, as shown in the following
|
||
example:
|
||
|
||
[source]
|
||
----
|
||
# Example of a custom Stub Downloader Provider
|
||
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
|
||
com.example.CustomStubDownloaderBuilder
|
||
----
|
||
|
||
Now you can pick a folder with the source of your stubs.
|
||
|
||
IMPORTANT: If you do not provide any implementation, then the default is used (scan classpath).
|
||
If you provide the `stubsMode = StubRunnerProperties.StubsMode.LOCAL` or
|
||
`, stubsMode = StubRunnerProperties.StubsMode.REMOTE` then the Aether implementation will be used
|
||
If you provide more than one, then the first one on the list is used.
|
||
|
||
[[scm-stub-downloader]]
|
||
=== Using the SCM Stub Downloader
|
||
|
||
Whenever the `repositoryRoot` starts with a SCM protocol
|
||
(currently we support only `git://`), the stub downloader will try
|
||
to clone the repository and use it as a source of contracts
|
||
to generate tests or stubs.
|
||
|
||
Either via environment variables, system properties, properties set
|
||
inside the plugin or contracts repository configuration you can
|
||
tweak the downloader's behaviour. Below you can find the list of
|
||
properties
|
||
|
||
.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`
|
||
|===
|
||
|
||
[[pact-stub-downloader]]
|
||
=== 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
|
||
|
||
.SCM Stub Downloader properties
|
||
|===
|
||
|Type of a property |Name of the property | 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)
|
||
|
|
||
|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)
|
||
|
|
||
|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
|
||
|===
|