Working docs

This commit is contained in:
Marcin Grzejszczak
2019-07-25 14:40:21 +02:00
parent 66a71e72a4
commit 3d89adcf88
34 changed files with 3000 additions and 5477 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,3 @@
:branch: master
image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-contract?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/{branch}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"]
image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"]
:branch: master
:core_path: ../../..
:plugins_path: ../../../spring-cloud-contract-tools
:converters_path: {plugins_path}/spring-cloud-contract-converters
:verifier_root_path: {core_path}/spring-cloud-contract-verifier
:contract_spec_path: {core_path}/specs/spring-cloud-contract-spec-java
:contract_spec_tests_path: {core_path}/specs/spring-cloud-contract-spec
:samples_path: {core_path}/samples
:verifier_core_path: {verifier_root_path}
:stubrunner_core_path: {core_path}/spring-cloud-contract-stub-runner
:standalone_samples_path: {samples_path}/standalone/dsl
:standalone_messaging_samples_path: {samples_path}/standalone/messaging
:standalone_pact_path: {samples_path}/standalone/pact
:standalone_restdocs_path: {samples_path}/standalone/restdocs
:tests_path: {core_path}/tests
:samples_branch: 2.2.x
:samples_url: https://raw.githubusercontent.com/spring-cloud-samples/spring-cloud-contract-samples/{samples_branch}
:introduction_url: ${core_path}/../../
== Spring Cloud Contract
You always need confidence when pushing new features into a new application or service in
@@ -35,20 +12,9 @@ If you prefer to learn about the project by doing some tutorials, you can check
workshops under
https://cloud-samples.spring.io/spring-cloud-contract-samples/workshops.html[this link].
=== Spring Cloud Contract Verifier
== Project page
include::_verifier_introduction.adoc[]
include::_links.adoc[]
=== Spring Cloud Contract WireMock
include::_spring-cloud-wiremock.adoc[]
== Documentation
You can read more about Spring Cloud Contract Verifier by reading the
{documentation_url}[docs]
You can read more about Spring Cloud Contract by going to https://spring.io/projects/spring-cloud-contract[the project page]
== Contributing

View File

@@ -32,5 +32,6 @@
:samples_branch: 2.2.x
:samples_url: https://raw.githubusercontent.com/spring-cloud-samples/spring-cloud-contract-samples/{samples_branch}
:samples_code: https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/{samples_branch}/
:doc_samples: {core_path}/samples/wiremock-jetty
:wiremock_tests: {core_path}/spring-cloud-contract-wiremock
:introduction_url: {core_path}

View File

@@ -1,38 +1,157 @@
=== How It Works
include::_attributes.adoc[]
This section explores how Spring Cloud Contract.
If you are getting started with {project-full-name}, or "`Spring`" in general, start by reading
this section. It answers the basic "`what?`", "`how?`" and "`why?`" questions. It
includes an introduction to {project-full-name}, along with installation instructions. We then
walk you through building your first {project-full-name} application, discussing some core
principles as we go.
[[spring-cloud-contract-verifier-intro-three-second-tour]]
==== A Three-second Tour
[[getting-started-introducing-spring-cloud-contract]]
== Introducing Spring Cloud Contract
Spring Cloud Contract moves TDD to the level of software architecture. It allows to perform consumer, consumer-driven and producer-driven contract testing.
[[getting-started-introducing-spring-cloud-contract-history]]
=== History
Before becoming Spring Cloud Contract, this project was called https://github.com/Codearte/accurest[Accurest].
It was created by https://twitter.com/mgrzejszczak[Marcin Grzejszczak] and https://twitter.com/jkubrynski[Jakub Kubrynski]
from (https://github.com/Codearte[Codearte]).
The `0.1.0` release took place on 26 Jan 2015 and it became stable with `1.0.0` release on 29 Feb 2016.
[[getting-started-introducing-spring-cloud-contract-why]]
==== Why would you need it?
Assume that we have a system consisting of multiple microservices:
image::images/Deps.png[Microservices Architecture]
[[getting-started-introducing-spring-cloud-contract-testing-issues]]
==== Testing issues
If we wanted to test the application in top left corner to determine whether it can
communicate with other services, we could do one of two things:
- Deploy all microservices and perform end-to-end tests.
- Mock other microservices in unit/integration tests.
Both have their advantages but also a lot of disadvantages.
*Deploy all microservices and perform end to end tests*
Advantages:
- Simulates production.
- Tests real communication between services.
Disadvantages:
- To test one microservice, we have to deploy 6 microservices, a couple of databases,
etc.
- The environment where the tests run is locked for a single suite of tests (nobody else
would be able to run the tests in the meantime).
- They take a long time to run.
- The feedback comes very late in the process.
- They are extremely hard to debug.
*Mock other microservices in unit/integration tests*
Advantages:
- They provide very fast feedback.
- They have no infrastructure requirements.
Disadvantages:
- The implementor of the service creates stubs that might have nothing to do with
reality.
- You can go to production with passing tests and failing production.
To solve the aforementioned issues, Spring Cloud Contract was created. The main idea is to give you very fast feedback, without the need to set up the
whole world of microservices. If you work on stubs, then the only applications you need
are those that your application directly uses.
image::images/Stubs2.png[Stubbed Services]
Spring Cloud Contract gives you the certainty that the stubs that you use were
created by the service that you're calling. Also, if you can use them, it means that they
were tested against the producer's side. In short, you can trust those stubs.
[[getting-started-introducing-spring-cloud-contract-purposes]]
=== Purposes
The main purposes of Spring Cloud Contract are:
- To ensure that HTTP / Messaging stubs (used when developing the client) do exactly
what the actual server-side implementation does.
- To promote ATDD (acceptance test driven developement) method and Microservices architectural style.
- To provide a way to publish changes in contracts that are immediately visible on both sides.
- To generate boilerplate test code to be used on the server side.
By default Spring Cloud Contract integrates with http://wiremock.org[Wiremock] as the HTTP server stub.
IMPORTANT: Spring Cloud Contract's purpose is NOT to start writing business
features in the contracts. Assume that we have a business use case of fraud check. If a
user can be a fraud for 100 different reasons, we would assume that you would create 2
contracts, one for the positive case and one for the negative case. Contract tests are
used to test contracts between applications and not to simulate full behavior.
[[getting-started-what-is-a-contract]]
=== What is a contract?
As consumers of services, we need to define what exactly we want to achieve. We need to
formulate our expectations. That is why we write contracts. In other words a contract is an agreement on how the API / message communication should look like. Let's look at the following example
Assume that you want to send a request containing the ID of a client company and the
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
the PUT method.
====
[source,groovy,indent=0,role="primary"]
.groovy
----
include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
----
[source,yaml,indent=0,role="secondary"]
.yaml
----
include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[]
----
====
[[getting-started-three-second-tour]]
== A Three-second Tour
This very brief tour walks through using Spring Cloud Contract:
* <<spring-cloud-contract-verifier-intro-three-second-tour-producer>>
* <<spring-cloud-contract-verifier-intro-three-second-tour-consumer>>
* <<getting-started-three-second-tour-producer>>
* <<getting-started-three-second-tour-consumer>>
You can find a somewhat longer tour
<<spring-cloud-contract-verifier-intro-three-minute-tour,here>>.
<<getting-started-first-application,here>>.
[plantuml, three-second, png]
[plantuml, getting-started-three-second, png]
----
"API Producer"->"API Producer": add Spring Cloud \nContract (SCC) plugin
"API Producer"->"API Producer": add SCC Verifier dependency
"API Producer"->"API Producer": define contracts
"API Producer"->"Build": run build
"Build"->"SCC Plugin": generate \ntests, stubs and stubs \nartifact (e.g. stubs-jar)
"Build"->"Nexus / Artifactory": upload contracts \nand stubs and the project arifact
"Build"->"Stub Storage": upload contracts \nand stubs and the project arifact
"Build"->"API Producer": Build successful
"API Consumer"->"API Consumer": add SCC Stub Runner \ndependency
"API Consumer"->"API Consumer": write a SCC Stub Runner \nbased contract test
"SCC Stub Runner"->"Nexus / Artifactory": test asks for [API Producer] stubs
"Nexus / Artifactory"->"SCC Stub Runner": fetch the [API Producer] stubs
"SCC Stub Runner"->"Stub Storage": test asks for [API Producer] stubs
"Stub Storage"->"SCC Stub Runner": fetch the [API Producer] stubs
"SCC Stub Runner"->"SCC Stub Runner": run in memory\n HTTP server stubs
"API Consumer"->"SCC Stub Runner": send a request \nto the HTTP server stub
"SCC Stub Runner"->"API Consumer": communication is correct
----
[[spring-cloud-contract-verifier-intro-three-second-tour-producer]]
===== On the Producer Side
[[getting-started-three-second-tour-producer]]
=== On the Producer Side
To start working with Spring Cloud Contract, add files with `REST`/messaging contracts
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
@@ -77,8 +196,8 @@ application and the stub artifacts are built and installed in the local Maven re
The changes can now be merged, and both the application and the stub artifacts may be
published in an online repository.
[[spring-cloud-contract-verifier-intro-three-second-tour-consumer]]
===== On the Consumer Side
[[getting-started-three-second-tour-consumer]]
=== On the Consumer Side
`Spring Cloud Contract Stub Runner` can be used in the integration tests to get a running
WireMock instance or messaging route that simulates the actual service.
@@ -110,7 +229,7 @@ Stub Runner` properties, as shown in the following example:
+
[source,yaml,indent=0]
----
include::{github-raw}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
include::{introduction_url}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
----
Now you can annotate your test class with `@AutoConfigureStubRunner`. In the annotation,
@@ -132,21 +251,46 @@ TIP: Use the `REMOTE` `stubsMode` when downloading stubs from an online reposito
Now, in your integration test, you can receive stubbed versions of HTTP responses or
messages that are expected to be emitted by the collaborator service.
[[spring-cloud-contract-verifier-intro-three-minute-tour]]
==== A Three-minute Tour
[[getting-started-first-application]]
== Developing Your First Spring Cloud Contract based application
This brief tour walks through using Spring Cloud Contract:
* <<spring-cloud-contract-verifier-intro-three-minute-tour-producer>>
* <<spring-cloud-contract-verifier-intro-three-minute-tour-consumer>>
* <<getting-started-first-application-producer>>
* <<getting-started-first-application-consumer>>
You can find an even more brief tour
<<spring-cloud-contract-verifier-intro-three-second-tour,here>>.
<<getting-started-three-second-tour,here>>.
[[spring-cloud-contract-verifier-intro-three-minute-tour-producer]]
===== On the Producer Side
For the sake of this example the `Stub Storage` will be Nexus / Artifactory.
To start working with `Spring Cloud Contract`, add files with `REST/` messaging contracts
image::getting-started-three-second.png[Getting started first application]
[[getting-started-first-application-producer]]
=== On the Producer Side
To start working with `Spring Cloud Contract`, add Spring Cloud Contract Verifier dependency and plugin to your build file,
as shown in the following example:
[source,xml,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=verifier_test_dependencies,indent=0]
----
The following listing shows how to add the plugin, which should go in the build/plugins
portion of the file:
[source,xml,indent=0]
----
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
</plugin>
----
Now let's add files with `REST/` messaging contracts
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
`contractsDslDir` property. By default, it is `$rootDir/src/test/resources/contracts`.
@@ -234,35 +378,22 @@ include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.
----
====
Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file,
as shown in the following example:
[source,xml,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=verifier_test_dependencies,indent=0]
----
The following listing shows how to add the plugin, which should go in the build/plugins
portion of the file:
[source,xml,indent=0]
----
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
</plugin>
----
Running `./mvnw clean install` automatically generates tests that verify the application
compliance with the added contracts. By default, the generated tests are under
`org.springframework.cloud.contract.verifier.tests.`.
The following example shows a sample auto-generated test for an HTTP contract:
The generated tests will differ, depending on which framework and test type you've setup in your plugin.
[source,java,indent=0]
In the samples below you'll find:
- the default test mode for HTTP contracts via `MockMvc`
- JAX-RS client via the `JAXRS` test mode
- `WebTestClient` based test (this is particularly recommended while working with Reactive, `Web-Flux`-based applications) set via `WEBTESTCLIENT` test mode
- Spock based test via the `testFramework` property set to `SPOCK`
====
[source,java,indent=0,role="primary"]
.mockmvc
----
@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
@@ -285,18 +416,45 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
}
----
The preceding example uses Spring's `MockMvc` to run the tests. This is the default test
mode for HTTP contracts. However, JAX-RS client and explicit HTTP invocations can also be
used. (To do so, change the `testMode` property of the plugin to `JAX-RS` or `EXPLICIT`,
respectively.)
[source,java,indent=0,role="secondary"]
.jaxrs
----
@SuppressWarnings("rawtypes")
public class FooTest {
WebTarget webTarget;
Since 2.1.0, it is also possible to use `RestAssuredWebTestClient`with Spring's reactive `WebTestClient`
run under the hood. This is particularly recommended while working with Reactive, `Web-Flux`-based applications.
In order to use `WebTestClient` set `testMode` to `WEBTESTCLIENT`.
@Test
public void validate_() throws Exception {
Here is an example of a test generated in `WEBTESTCLIENT` test mode:
// when:
Response response = webTarget
.path("/users")
.queryParam("limit", "10")
.queryParam("offset", "20")
.queryParam("filter", "email")
.queryParam("sort", "name")
.queryParam("search", "55")
.queryParam("age", "99")
.queryParam("name", "Denis.Stepanov")
.queryParam("email", "bob@email.com")
.request()
.build("GET")
.invoke();
String responseAsString = response.readEntity(String.class);
[source,java,indent=0]
// then:
assertThat(response.getStatus()).isEqualTo(200);
// and:
DocumentContext parsedJson = JsonPath.parse(responseAsString);
assertThatJson(parsedJson).field("['property1']").isEqualTo("a");
}
}
----
[source,java,indent=0,role="secondary"]
.webtestclient
----
@Test
public void validate_shouldRejectABeerIfTooYoung() throws Exception {
@@ -318,15 +476,8 @@ Here is an example of a test generated in `WEBTESTCLIENT` test mode:
}
----
Apart from the default JUnit 4, you can instead use JUnit 5 or Spock tests, by setting the plugin
`testFramework` property to either `JUNIT5` or `Spock`.
TIP: You can now also generate WireMock scenarios based on the contracts, by including an
order number followed by an underscore at the beginning of the contract file names.
The following example shows an auto-generated test in Spock for a messaging stub contract:
[source,groovy,indent=0]
[source,groovy,indent=0,role="secondary"]
.spock
----
given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
@@ -341,6 +492,7 @@ then:
noExceptionThrown()
bookWasDeleted()
----
====
As the implementation of the functionalities described by the contracts is not yet
present, the tests fail.
@@ -375,18 +527,8 @@ shown in the following example:
You can now merge the changes and publish both the application and the stub artifacts
in an online repository.
*Docker Project*
In order to enable working with contracts while creating applications in non-JVM
technologies, the `springcloud/spring-cloud-contract` Docker image has been created. It
contains a project that automatically generates tests for HTTP contracts and executes them
in `EXPLICIT` test mode. Then, if the tests pass, it generates Wiremock stubs and,
optionally, publishes them to an artifact manager. In order to use the image, you can
mount the contracts into the `/contracts` directory and set a few environment variables.
// TODO: We should answer the obvious question: Which environment variables?
[[spring-cloud-contract-verifier-intro-three-minute-tour-consumer]]
===== On the Consumer Side
[[getting-started-first-application-consumer]]
=== On the Consumer Side
`Spring Cloud Contract Stub Runner` can be used in the integration tests to get a running
WireMock instance or messaging route that simulates the actual service.
@@ -417,7 +559,7 @@ Runner` properties, as shown in the following example:
+
[source,yaml,indent=0]
----
include::{github-raw}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
include::{introduction_url}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
----
Now you can annotate your test class with `@AutoConfigureStubRunner`. In the annotation,
@@ -451,88 +593,8 @@ to the following in the build logs:
2016-07-19 14:22:27.737 INFO 41050 --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]
----
==== Defining the Contract
As consumers of services, we need to define what exactly we want to achieve. We need to
formulate our expectations. That is why we write contracts.
Assume that you want to send a request containing the ID of a client company and the
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
the PUT method.
====
[source,groovy,indent=0,role="primary"]
.groovy
----
include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
----
[source,yaml,indent=0,role="secondary"]
.yaml
----
include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[]
----
====
==== Client Side
Spring Cloud Contract generates stubs, which you can use during client-side testing.
You get a running WireMock instance/Messaging route that simulates the service.
You would like to feed that instance with a proper stub definition.
At some point in time, you need to send a request to the Fraud Detection service.
[source,groovy,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=client_call_server,indent=0]
----
Annotate your test class with `@AutoConfigureStubRunner`. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.
[source,groovy,indent=0]
----
include::{introduction_url}/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=autoconfigure_stubrunner,indent=0]
----
After that, during the tests, Spring Cloud Contract automatically finds the stubs
(simulating the real service) in the Maven repository and exposes them on a configured
(or random) port.
==== Server Side
Since you are developing your stub, you need to be sure that it actually resembles your
concrete implementation. You cannot have a situation where your stub acts in one way and
your application behaves in a different way, especially in production.
To ensure that your application behaves the way you define in your stub, tests are
generated from the stub you provide.
The autogenerated test looks, more or less, like this:
[source,java,indent=0]
----
@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/vnd.fraud.v1+json")
.body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
// when:
ResponseOptions response = given().spec(request)
.put("/fraudcheck");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
}
----
=== Step-by-step Guide to Consumer Driven Contracts (CDC)
[[getting-started-cdc]]
== Step-by-step Guide to Consumer Driven Contracts (CDC) with contracts laying on the producer side
Consider an example of Fraud Detection and the Loan Issuance process. The business
scenario is such that we want to issue loans to people but do not want them to steal from
@@ -542,11 +604,19 @@ Assume that `Loan Issuance` is a client to the `Fraud Detection` server. In the
sprint, we must develop a new feature: if a client wants to borrow too much money, then
we mark the client as a fraud.
Technical remark - Fraud Detection has an `artifact-id` of `http-server`, while Loan
Issuance has an artifact-id of `http-client`, and both have a `group-id` of `com.example`.
Technical remarks
Social remark - both client and server development teams need to communicate directly and
discuss changes while going through the process. CDC is all about communication.
* Fraud Detection has an `artifact-id` of `http-server`,
* Loan
Issuance has an artifact-id of `http-client`
* both have a `group-id` of `com.example`
* For the sake of this example the `Stub Storage` will be Nexus / Artifactory
Social remarks
* both client and server development teams need to communicate directly and
discuss changes while going through the process
* CDC is all about communication.
The https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples/standalone/dsl/http-server[server
side code is available here] and https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples/standalone/dsl/http-client[the
@@ -555,7 +625,8 @@ client code here].
TIP: In this case, the producer owns the contracts. Physically, all the contract are
in the producer's repository.
==== Technical note
[[getting-started-cdc-technical-note]]
=== Technical note
If using the *SNAPSHOT* / *Milestone* / *Release Candidate* versions please add the
following section to your build:
@@ -572,7 +643,13 @@ include::{introduction_url}/samples/standalone/dsl/http-server/pom.xml[tags=repo
include::{introduction_url}/samples/standalone/dsl/http-server/build.gradle[tags=deps_repos,indent=0]
----
==== Consumer side (Loan Issuance)
For simplicity we will use the following acronyms:
- Loan Issuance - LI - the http client
- Fraud Detection - FD - the http server
- Spring Cloud Contract - SCC
=== Consumer side (Loan Issuance)
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server), you might do the following steps:
@@ -580,7 +657,7 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s
. Write the missing implementation.
. Clone the Fraud Detection service repository locally.
. Define the contract locally in the repo of Fraud Detection service.
. Add the Spring Cloud Contract Verifier plugin.
. Add the Spring Cloud Contract (SCC) plugin.
. Run the integration tests.
. File a pull request.
. Create an initial implementation.
@@ -589,6 +666,36 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s
. Deploy your app.
. Work online.
Let's start with the Loan Issuance flow:
[plantuml, getting-started-cdc-client, png]
----
"Loan\nIssuance"->"Loan\nIssuance": start doing TDD\nby writing a test\nfor your feature
"Loan\nIssuance"->"Loan\nIssuance": write the \nmissing implementation
"Loan\nIssuance"->"Loan\nIssuance": run a test - it fails\ndue to no server running
"Loan\nIssuance"->"Fraud\nDetection\nClone": clone the repository
"Fraud\nDetection\nClone"->"Fraud\nDetection\nClone": add missing dependencies\n& define contracts
"Fraud\nDetection\nClone"->"Fraud\nDetection\nClone": add the SCC plugin
"Fraud\nDetection\nClone"->"FD \nClone Build": install the stubs locally
"FD \nClone Build"->"SCC Plugin \nin FD Clone": generate stubs \nand stubs \nartifact (e.g. stubs-jar)
"SCC Plugin \nin FD Clone"->"FD \nClone Build": stubs and artifacts\ngenerated
"FD \nClone Build"->"Local storage": install the stubs locally
"Local storage"->"FD \nClone Build": stub sucessfully installed
"FD \nClone Build"->"Fraud\nDetection\nClone": build successful
"Loan\nIssuance"->"Loan\nIssuance": add a SCC\nStub Runner\ndependency\nand setup
"Loan\nIssuance"->"LI\nSCC\nStub Runner": start stubs\nof FD from\nlocal storage
"LI\nSCC\nStub Runner"->"Local storage": find stubs of [FD]
"Local storage"->"LI\nSCC\nStub Runner": stubs of [FD] found
"LI\nSCC\nStub Runner"->"FD stub": run stubs of [FD]
"FD stub"->"LI\nSCC\nStub Runner": [FD] stub is running
"LI\nSCC\nStub Runner"->"Loan\nIssuance": stubs running and ready for the test
"Loan\nIssuance"->"Loan\nIssuance": run a test
"Loan\nIssuance"->"FD stub": the test\nsends a request\nto the running stub
"FD stub"->"Loan\nIssuance": stub responds successfuly
"Loan\nIssuance"->"Loan\nIssuance": the test passes successfully
"Loan\nIssuance"->"Fraud\nDetection": send a pull request\nwith the\nsuggested contracts
----
*Start doing TDD by writing a test for your feature.*
[source,groovy,indent=0]
@@ -656,6 +763,7 @@ of an identifier or a timestamp, you need not hardcode a value. You want to allo
different ranges of values. To enable ranges of values, you can set regular expressions
matching those values for the consumer side. You can provide the body by means of either
a map notation or String with interpolations.
//TODO: Ensure that the links are correct
Consult the <<contract-dsl>> section for more information. We highly recommend using the map notation!
TIP: You must understand the map notation in order to set up contracts. Please read the
@@ -793,11 +901,37 @@ you wish.
Once you are satisfied with the results and the test passes, publish a pull request to
the server side. Currently, the consumer side work is done.
==== Producer side (Fraud Detection server)
=== Producer side (Fraud Detection server)
As a developer of the Fraud Detection server (a server to the Loan Issuance service):
As a developer of the Fraud Detection server (a server to the Loan Issuance service) we would like to:
*Create an initial implementation.*
- take over the pull request
- write the missing implementation
- deploy the application
Let's look at the Fraud Detection flow:
[plantuml, getting-started-cdc-server, png]
----
"Fraud\nDetection"->"Fraud\nDetection": take over the\n pull request
"Fraud\nDetection"->"Fraud\nDetection": setup\nSpring Cloud\nContract plugin
"Fraud\nDetection"->"Fraud\nDetection\nBuild": run the build
"Fraud\nDetection\nBuild"->"SCC Plugin": generate tests\nstubs \nand stubs artifact \n(e.g. stubs-jar)
"SCC Plugin"->"Fraud\nDetection\nBuild": tests and stubs generated
"Fraud\nDetection\nBuild"->"Fraud\nDetection\nBuild": run tests
"Fraud\nDetection\nBuild"->"Fraud\nDetection": generated tests failed!
"Fraud\nDetection"->"Fraud\nDetection": setup\nbase classes\nfor contract tests
"Fraud\nDetection"->"Fraud\nDetection\nBuild": run the build
"Fraud\nDetection\nBuild"->"SCC Plugin": generate tests\nstubs \nand stubs artifact \n(e.g. stubs-jar)
"SCC Plugin"->"Fraud\nDetection\nBuild": tests and stubs generated
"Fraud\nDetection\nBuild"->"Fraud\nDetection\nBuild": run tests
"Fraud\nDetection\nBuild"->"Fraud\nDetection": all the tests passed!
"Fraud\nDetection"->"Fraud\nDetection": commit and push changes
"Fraud\nDetection"->"CI": commit pushed!\nTriggers the build
"CI"->"Stub Storage": build successful,\nupload artifacts
----
*Take over the pull request.*
As a reminder, you can see the initial implementation here:
@@ -808,8 +942,6 @@ include::{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com
}
----
*Take over the pull request.*
[source,bash,indent=0]
----
$ git checkout -b contract-change-pr master
@@ -924,9 +1056,29 @@ $ git push origin master
Your CI might run something like `./mvnw clean deploy`, which would publish both the
application and the stub artifacts.
==== Consumer Side (Loan Issuance) Final Step
=== Consumer Side (Loan Issuance) Final Step
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server) we will want to:
- merge our feature branch to `master`
- switch to online mode of working
[plantuml, getting-started-cdc-client, png]
----
"Loan\nIssuance"->"Loan\nIssuance": merge the\nfeature branch\nto master branch
"Loan\nIssuance"->"Loan\nIssuance": setup SCC Stub Runner\nto fetch stubs\nfrom Stub Storage
"Loan\nIssuance"->"LI\nSCC\nStub Runner": start stubs\nof FD from\nStub Storage
"LI\nSCC\nStub Runner"->"Stub Storage": find stubs of [FD]
"Stub Storage"->"LI\nSCC\nStub Runner": stubs of [FD] found
"LI\nSCC\nStub Runner"->"FD stub": run stubs of [FD]
"FD stub"->"LI\nSCC\nStub Runner": [FD] stub is running
"LI\nSCC\nStub Runner"->"Loan\nIssuance": stubs running and ready for the test
"Loan\nIssuance"->"Loan\nIssuance": run a test
"Loan\nIssuance"->"FD stub": the test\nsends a request\nto the running stub
"FD stub"->"Loan\nIssuance": stub responds successfuly
"Loan\nIssuance"->"Loan\nIssuance": the test passes successfully
----
*Merge branch to master.*
@@ -946,7 +1098,40 @@ achieving the same thing by changing the properties.
[source,yaml,indent=0]
----
include::{github-raw}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
include::{introduction_url}/samples/standalone/dsl/http-client/src/test/resources/application-test-repo.yaml[]
----
That's it!
That's it!
[[getting-started-whats-next]]
== What to Read Next
Hopefully, this section provided some of the {project-full-name} basics and got you on your way
to writing your own applications. If you are a task-oriented type of developer, you might
want to jump over to https://spring.io and check out some of the
https://spring.io/guides/[getting started] guides that solve specific "`How do I do that
with Spring?`" problems. We also have {project-full-name}-specific
"`<<howto.adoc#howto, How-to>>`" reference documentation.
Otherwise, the next logical step is to read _<<using.adoc#using>>_. If
you are really impatient, you could also jump ahead and read about
_<<project-features.adoc#project-features, {project-full-name} features>>_.
// TODO: Check if we can link courses
In addition to that you can check out the following videos:
- "Consumer Driven Contracts and Your Microservice Architecture" by Olga Maciaszek-Sharma and Marcin Grzejszczak
video::pDkC_00hhvA[youtube,width=640,height=480]
- "Contract Tests in the Enterprise" by Marcin Grzejszczak
video::ZyHG-VOzPZg[youtube,width=640,height=480]
- "Why Contract Tests Matter?" by Marcin Grzejszczak
video::TvpkZu1e2Dc[youtube,start=6262,width=640,height=480]
You can find the default project samples at
https://github.com/spring-cloud-samples/spring-cloud-contract-samples[samples].
You can find the Spring Cloud Contract workshops https://cloud-samples.spring.io/spring-cloud-contract-samples/[over here].

View File

@@ -1,11 +0,0 @@
== Links
The following links may be helpful when working with Spring Cloud Contract:
* https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract Github
Repository]
* https://github.com/spring-cloud-samples/spring-cloud-contract-samples/[Spring Cloud
Contract Samples]
* https://gitter.im/spring-cloud/spring-cloud-contract[Spring Cloud Contract Gitter]
* https://www.youtube.com/watch?v=sAAklvxmPmk[Spring Cloud Contract WJUG Presentation by
Marcin Grzejszczak]

View File

@@ -0,0 +1,417 @@
[[features-jax-rs]]
== JAX-RS
include::_attributes.adoc[]
The Spring Cloud Contract 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]
----
[[contract-webflux]]
== WebFlux
Spring Cloud Contract offers two ways of working with WebFlux.
[[contract-webtestclient]]
=== WebFlux with WebTestClient
One of them is via the `WebTestClient` 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>WEBTESTCLIENT</testMode>
</configuration>
</plugin>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
contracts {
testMode = 'WEBTESTCLIENT'
}
----
====
The following example shows how to set up a `WebTestClient` base class and `RestAssured`
for WebFlux:
[source,groovy,indent=0]
----
import io.restassured.module.webtestclient.RestAssuredWebTestClient;
import org.junit.Before;
public abstract class BeerRestBase {
@Before
public void setup() {
RestAssuredWebTestClient.standaloneSetup(
new ProducerController(personToCheck -> personToCheck.age >= 20));
}
}
}
----
[[contract-webflux-explicit]]
=== WebFlux with Explicit mode
Another way is with the `EXPLICIT` mode in your generated tests
to work with WebFlux.
====
[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'
}
----
====
The following example shows how to set up a base class and Rest Assured for Web Flux:
[source,groovy,indent=0]
----
include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=annotations,indent=0]
// your tests go here
// in this config class you define all controllers and mocked services
include::{samples_url}/producer_webflux/src/test/java/com/example/BeerRestBase.java[tags=config,indent=0]
}
----
[[features-context-paths]]
=== 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`).
[[features-rest-docs]]
=== Working with REST Docs
https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
or `WebTestClient` or Rest Assured. At the same time that you generate documentation for your API, you can also
generate WireMock stubs by using Spring Cloud Contract WireMock. To do so, write your
normal REST Docs test cases and use `@AutoConfigureRestDocs` to have stubs be
automatically generated in the REST Docs output directory.
[plantuml, rest-docs, png]
----
"API Producer"->"API Producer": add Spring Cloud Contract (SCC) \nStub Runner dependency
"API Producer"->"API Producer": setup stub jar assembly
"API Producer"->"API Producer": write and setup REST Docs tests
"API Producer"->"Build": run build
"Build"->"REST Docs": generate API \ndocumentation
"REST Docs"->"SCC": generate stubs from the \nREST Docs tests
"REST Docs"->"SCC": generate contracts from the \nREST Docs tests
"Build"->"Build": assemble stubs jar with \nstubs and contracts
"Build"->"Nexus / Artifactory": upload contracts \nand stubs and the project arifact
"Build"->"API Producer": Build successful
"API Consumer"->"API Consumer": add SCC Stub Runner \ndependency
"API Consumer"->"API Consumer": write a SCC Stub Runner \nbased contract test
"SCC Stub Runner"->"Nexus / Artifactory": test asks for [API Producer] stubs
"Nexus / Artifactory"->"SCC Stub Runner": fetch the [API Producer] stubs
"SCC Stub Runner"->"SCC Stub Runner": run in memory\n HTTP server stubs
"API Consumer"->"SCC Stub Runner": send a request \nto the HTTP server stub
"SCC Stub Runner"->"API Consumer": communication is correct
----
The following code shows an
example using `MockMvc`:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(get("/resource"))
.andExpect(content().string("Hello World"))
.andDo(document("resource"));
}
}
----
This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches
all GET requests to the "/resource" path. The same example with `WebTestClient` (used
for testing Spring WebFlux applications) would look like this:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureWebTestClient
public class ApplicationTests {
@Autowired
private WebTestClient client;
@Test
public void contextLoads() throws Exception {
client.get().uri("/resource").exchange()
.expectBody(String.class).isEqualTo("Hello World")
.consumeWith(document("resource"));
}
}
----
Without any additional configuration, these tests create a stub with a request matcher
for the HTTP method and all headers except "host" and "content-length". To match the
request more precisely (for example, to match the body of a POST or PUT), we need to
explicitly create a request matcher. Doing so has two effects:
* Creating a stub that matches only in the way you specify.
* Asserting that the request in the test case also matches the same conditions.
The main entry point for this feature is `WireMockRestDocs.verify()`, which can be used
as a substitute for the `document()` convenience method, as shown in the following
example:
[source,java,indent=0]
import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify().jsonPath("$.id")
.stub("resource"));
}
}
----
This contract specifies that any valid POST with an "id" field receives the response
defined in this test. You can chain together calls to `.jsonPath()` to add additional
matchers. If JSON Path is unfamiliar, The https://github.com/jayway/JsonPath[JayWay
documentation] can help you get up to speed. The `WebTestClient` version of this test
has a similar `verify()` static helper that you insert in the same place.
Instead of the `jsonPath` and `contentType` convenience methods, you can also use the
WireMock APIs to verify that the request matches the created stub, as shown in the
following example:
[source,java,indent=0]
----
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify()
.wiremock(WireMock.post(
urlPathEquals("/resource"))
.withRequestBody(matchingJsonPath("$.id"))
.stub("post-resource"));
}
----
The WireMock API is rich. You can match headers, query parameters, and request body by
regex as well as by JSON path. These features can be used to create stubs with a wider
range of parameters. The above example generates a stub resembling the following example:
.post-resource.json
[source,json]
----
{
"request" : {
"url" : "/resource",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$.id"
}]
},
"response" : {
"status" : 200,
"body" : "Hello World",
"headers" : {
"X-Application-Context" : "application:-1",
"Content-Type" : "text/plain"
}
}
}
----
NOTE: You can use either the `wiremock()` method or the `jsonPath()` and `contentType()`
methods to create request matchers, but you can't use both approaches.
On the consumer side, you can make the `resource.json` generated earlier in this section
available on the classpath (by
<<features-stub-runner-publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a
number of different ways, including by using
`@AutoConfigureWireMock(stubs="classpath:resource.json")`, as described earlier in this
document.
[[features-rest-docs-contracts]]
==== Generating Contracts viaREST Docs
You can also generate Spring Cloud Contract DSL files and documentation with Spring REST
Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts
and the stubs.
Why would you want to use this feature? Some people in the community asked questions
about a situation in which they would like to move to DSL-based contract definition,
but they already have a lot of Spring MVC tests. Using this feature lets you generate
the contract files that you can later modify and move to folders (defined in your
configuration) so that the plugin finds them.
TIP: You might wonder why this functionality is in the WireMock module. The functionality
is there because it makes sense to generate both the contracts and the stubs.
Consider the following test:
[source,java]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java[tags=contract_snippet]
----
The preceding test creates the stub presented in the previous section, generating both
the contract and a documentation file.
The contract is called `index.groovy` and might look like the following example:
[source,groovy]
----
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'POST'
url '/foo'
body('''
{"foo": 23 }
''')
headers {
header('''Accept''', '''application/json''')
header('''Content-Type''', '''application/json''')
}
}
response {
status OK()
body('''
bar
''')
headers {
header('''Content-Type''', '''application/json;charset=UTF-8''')
header('''Content-Length''', '''3''')
}
testMatchers {
jsonPath('$[?(@.foo >= 20)]', byType())
}
}
}
----
The generated document (formatted in Asciidoc in this case) contains a formatted
contract. The location of this file would be `index/dsl-contract.adoc`.

View File

@@ -0,0 +1,922 @@
include::_attributes.adoc[]
[[features-messaging]]
== Messaging
Spring Cloud Contract lets you verify applications that use messaging as a
means of communication. All of the integrations shown in this document work with Spring,
but you can also create one of your own and use that.
[[contract-dsl-messaging-top-level]]
=== Messaging DSL 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-messaging-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:
====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.groovy
----
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=method_trigger,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.yml
----
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:
====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.groovy
----
include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=message_trigger,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.yml
----
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-messaging-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.
[[features-messaging-integrations]]
=== Integrations
You can use one of the following four integration configurations:
* Apache Camel
* Spring Integration
* Spring Cloud Stream
* Spring AMQP
Since we use Spring Boot, if you have added one of these libraries to the classpath, all
the messaging configuration is automatically set up.
IMPORTANT: Remember to put `@AutoConfigureMessageVerifier` on the base class of your
generated tests. Otherwise, messaging part of Spring Cloud Contract does not
work.
IMPORTANT: If you want to use Spring Cloud Stream, remember to add a dependency on
`org.springframework.cloud:spring-cloud-stream-test-support`, as shown below
=====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
testCompile "org.springframework.cloud:spring-cloud-stream-test-support"
----
=====
[[features-messaging-manual]]
==== Manual Integration Testing
The main interface used by the tests is
`org.springframework.cloud.contract.verifier.messaging.MessageVerifier`.
It defines how to send and receive messages. You can create your own implementation to
achieve the same goal.
In a test, you can inject a `ContractVerifierMessageExchange` to send and receive
messages that follow the contract. Then add `@AutoConfigureMessageVerifier` to your test.
Here's an example:
[source,java,indent=0]
----
@RunWith(SpringTestRunner.class)
@SpringBootTest
@AutoConfigureMessageVerifier
public static class MessagingContractTests {
@Autowired
private MessageVerifier verifier;
...
}
----
NOTE: If your tests require stubs as well, then `@AutoConfigureStubRunner` includes the
messaging configuration, so you only need the one annotation.
[[features-messaging-test-generation]]
=== Messaging Producer Side Test Generation
Having the `input` or `outputMessage` sections in your DSL results in creation of tests
on the publisher's side. By default, JUnit 4 tests are created. However, there is also a
possibility to create JUnit 5, TestNG or Spock tests.
There are 3 main scenarios that we should take into consideration:
* Scenario 1: There is no input message that produces an output message. The output
message is triggered by a component inside the application (for example, scheduler).
* Scenario 2: The input message triggers an output message.
* Scenario 3: The input message is consumed and there is no output message.
IMPORTANT: The destination passed to `messageFrom` or `sentTo` can have different
meanings for different messaging implementations. For *Stream* and *Integration* it is
first resolved as a `destination` of a channel. Then, if there is no such `destination`
it is resolved as a channel name. For *Camel*, that's a certain component (for example,
`jms`).
[[features-messaging-scenario1]]
==== Scenario 1: No Input Message
For the given contract:
=====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.groovy
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_dsl]
----
[source,yml,indent=0,subs="verbatim,attributes",role="secondary"]
.yml
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario1.yml[indent=0]
----
=====
The following test would be created
====
[source,java,indent=0,subs="verbatim,attributes",role="primary"]
.JUnit
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_junit_test]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Spock
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_test]
----
====
[[features-messaging-scenario2]]
==== Scenario 2: Output Triggered by Input
For the given contract:
=====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.groovy
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_dsl]
----
[source,yml,indent=0,subs="verbatim,attributes",role="secondary"]
.yml
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario2.yml[indent=0]
----
=====
The following test would be created
====
[source,java,indent=0,subs="verbatim,attributes",role="primary"]
.JUnit
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_junit]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Spock
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_spock]
----
====
[[features-messaging-scenario3]]
==== Scenario 3: No Output Message
For the given contract:
====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.groovy
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl]
----
[source,yml,indent=0,subs="verbatim,attributes",role="secondary"]
.yml
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0]
----
====
The following test would be created
====
[source,java,indent=0,subs="verbatim,attributes",role="primary"]
.JUnit
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_junit]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Spock
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_spock]
----
====
[[features-messaging-consumer]]
=== Consumer Stub Generation
Unlike the HTTP part, in messaging, we need to publish the contract definition inside the JAR with
a stub. Then it is parsed on the consumer side and proper stubbed routes are created.
IMPORTANT: If you have multiple frameworks on the classpath Stub Runner will need to
define which one should be used. Let's assume that you have both AMQP, Spring Cloud Stream and Spring Integration
on the classpath. Then you need to set `stubrunner.stream.enabled=false` and `stubrunner.integration.enabled=false`.
That way the only remaining framework is Spring AMQP.
[[features-messaging-stub-triggering]]
==== Stub triggering
To trigger a message, use the `StubTrigger` interface:
[source,groovy]
----
include::{stubrunner_core_path}/src/main/java/org/springframework/cloud/contract/stubrunner/StubTrigger.java[lines=16..-1]
----
For convenience, the `StubFinder` interface extends `StubTrigger`, so you only need one
or the other in your tests.
`StubTrigger` gives you the following options to trigger a message:
* <<features-messaging-trigger-label>>
* <<features-messaging-trigger-group-artifact-ids>>
* <<features-messaging-trigger-artifact-ids>>
* <<features-messaging-trigger-all-messages>>
[[features-messaging-trigger-label]]
==== Trigger by Label
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
[[features-messaging-trigger-group-artifact-ids]]
==== Trigger by Group and Artifact Ids
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_group_artifact,indent=0]
----
[[features-messaging-trigger-artifact-ids]]
==== Trigger by Artifact Ids
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_artifact,indent=0]
----
[[features-messaging-trigger-all-messages]]
==== Trigger All Messages
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_all,indent=0]
----
:input_name: jms:input
:output_name: jms:output
[[features-messaging-stub-runner-camel]]
=== Messaging Consumer Side With Apache Camel
Spring Cloud Contract Stub Runner's messaging module gives you an easy way to integrate with Apache Camel.
For the provided artifacts it will automatically download the stubs and register the required
routes.
[[features-messaging-stub-runner-camel-adding]]
==== Adding it to the project
It's enough to have both Apache Camel and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with `@AutoConfigureStubRunner`.
[[features-messaging-stub-runner-camel-disabling]]
==== Disabling the functionality
If you need to disable this functionality just pass `stubrunner.camel.enabled=false` property.
[[features-messaging-stub-runner-camel-example]]
==== Examples
Let us assume that we have the following Maven repository with a deployed stubs for the
`camelService` application.
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── camelService
├── 0.0.1-SNAPSHOT
│   ├── camelService-0.0.1-SNAPSHOT.pom
│   ├── camelService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
And the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Let's consider the following contracts (let' number it with *1*):
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
and number *2*
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
[[features-messaging-stub-runner-camel-scenario1]]
===== Scenario 1 (no input message)
So as to trigger a message via the `return_book_1` label we'll use the `StubTigger` interface as follows
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
Next we'll want to listen to the output of the message sent to `{output_name}`
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
And the received message would pass the following assertions
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
[[features-messaging-stub-runner-camel-scenario2]]
===== Scenario 2 (output triggered by input)
Since the route is set for you it's enough to just send a message to the `{output_name}` destination.
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_send,indent=0]
----
Next we'll want to listen to the output of the message sent to `{output_name}`
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
And the received message would pass the following assertions
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
[[features-messaging-stub-runner-camel-scenario3]]
===== Scenario 3 (input with no output)
Since the route is set for you it's enough to just send a message to the `{output_name}` destination.
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----
:input_name: input
:output_name: output
[[features-messaging-stub-runner-integration]]
=== Messaging Consumer Side With Spring Integration
Spring Cloud Contract Stub Runner's messaging module gives you an easy way to
integrate with Spring Integration. For the provided artifacts, it automatically downloads
the stubs and registers the required routes.
[[features-messaging-stub-runner-integration-adding]]
==== Adding the Runner to the Project
You can have both Spring Integration and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with `@AutoConfigureStubRunner`.
[[features-messaging-stub-runner-integration-disabling]]
==== Disabling the functionality
If you need to disable this functionality, set the
`stubrunner.integration.enabled=false` property.
[[features-messaging-stub-runner-integration-example]]
==== Examples
Assume that you have the following Maven repository with deployed stubs for the
`integrationService` application:
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── integrationService
├── 0.0.1-SNAPSHOT
│   ├── integrationService-0.0.1-SNAPSHOT.pom
│   ├── integrationService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Consider the following contracts (numbered *1*):
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
Now consider *2*:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
and the following Spring Integration Route:
[source,xml]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/resources/integration-context.xml[lines=1;18..-1]
----
These examples lend themselves to three scenarios:
* <<features-messaging-stub-runner-integration-scenario1>>
* <<features-messaging-stub-runner-integration-scenario2>>
* <<features-messaging-stub-runner-integration-scenario3>>
[[features-messaging-stub-runner-integration-scenario1]]
===== Scenario 1 (no input message)
To trigger a message via the `return_book_1` label, use the `StubTigger` interface, as
follows:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
To listen to the output of the message sent to `{output_name}`:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
The received message would pass the following assertions:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
[[features-messaging-stub-runner-integration-scenario2]]
===== Scenario 2 (output triggered by input)
Since the route is set for you, you can send a message to the `{output_name}`
destination:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_send,indent=0]
----
To listen to the output of the message sent to `{output_name}`:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
[[features-messaging-stub-runner-integration-scenario3]]
===== Scenario 3 (input with no output)
Since the route is set for you, you can send a message to the `{input_name}` destination:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-integration/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----
[[features-messaging-stub-runner-stream]]
=== Messaging Consumer Side With Spring Cloud Stream
Spring Cloud Contract Stub Runner's messaging module gives you an easy way to
integrate with Spring Stream. For the provided artifacts, it automatically downloads the
stubs and registers the required routes.
WARNING: If Stub Runner's integration with Stream the `messageFrom` or `sentTo` Strings
are resolved first as a `destination` of a channel and no such `destination` exists, the
destination is resolved as a channel name.
IMPORTANT: If you want to use Spring Cloud Stream remember, to add a dependency on
`org.springframework.cloud:spring-cloud-stream-test-support`.
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
testCompile "org.springframework.cloud:spring-cloud-stream-test-support"
----
====
[[features-messaging-stub-runner-stream-adding]]
==== Adding the Runner to the Project
You can have both Spring Cloud Stream and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with `@AutoConfigureStubRunner`.
[[features-messaging-stub-runner-stream-disabling]]
==== Disabling the functionality
If you need to disable this functionality, set the `stubrunner.stream.enabled=false`
property.
[[features-messaging-stub-runner-stream-example]]
==== Examples
Assume that you have the following Maven repository with a deployed stubs for the
`streamService` application:
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── streamService
├── 0.0.1-SNAPSHOT
│   ├── streamService-0.0.1-SNAPSHOT.pom
│   ├── streamService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Consider the following contracts (numbered *1*):
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
Now consider *2*:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
Now consider the following Spring configuration:
[source,yaml]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/resources/application.yml[]
----
These examples lend themselves to three scenarios:
* <<features-messaging-stub-runner-stream-scenario1>>
* <<features-messaging-stub-runner-stream-scenario2>>
* <<features-messaging-stub-runner-stream-scenario3>>
[[features-messaging-stub-runner-stream-scenario1]]
===== Scenario 1 (no input message)
To trigger a message via the `return_book_1` label, use the `StubTrigger` interface as
follows:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
To listen to the output of the message sent to a channel whose `destination` is
`returnBook`:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
[[features-messaging-stub-runner-stream-scenario2]]
===== Scenario 2 (output triggered by input)
Since the route is set for you, you can send a message to the `bookStorage`
`destination`:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_send,indent=0]
----
To listen to the output of the message sent to `returnBook`:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
[[features-messaging-stub-runner-stream-scenario3]]
===== Scenario 3 (input with no output)
Since the route is set for you, you can send a message to the `{output_name}`
destination:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----
[[features-messaging-stub-runner-amqp]]
=== Messaging Consumer Side With Spring AMQP
Spring Cloud Contract Stub Runner's messaging module provides an easy way to
integrate with Spring AMQP's Rabbit Template. For the provided artifacts, it
automatically downloads the stubs and registers the required routes.
The integration tries to work standalone (that is, without interaction with a running
RabbitMQ message broker). It expects a `RabbitTemplate` on the application context and
uses it as a spring boot test named `@SpyBean`. As a result, it can use the mockito spy
functionality to verify and inspect messages sent by the application.
On the message consumer side, the stub runner considers all `@RabbitListener` annotated
endpoints and all `SimpleMessageListenerContainer` objects on the application context.
As messages are usually sent to exchanges in AMQP, the message contract contains the
exchange name as the destination. Message listeners on the other side are bound to
queues. Bindings connect an exchange to a queue. If message contracts are triggered, the
Spring AMQP stub runner integration looks for bindings on the application context that
match this exchange. Then it collects the queues from the Spring exchanges and tries to
find message listeners bound to these queues. The message is triggered for all matching
message listeners.
If you need to work with routing keys, it's enough to pass them via the `amqp_receivedRoutingKey`
messaging header.
[[features-messaging-stub-runner-amqp-adding]]
==== Adding the Runner to the Project
You can have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and
set the property `stubrunner.amqp.enabled=true`. Remember to annotate your test class
with `@AutoConfigureStubRunner`.
IMPORTANT: If you already have Stream and Integration on the classpath, you need
to disable them explicitly by setting the `stubrunner.stream.enabled=false` and
`stubrunner.integration.enabled=false` properties.
[[features-messaging-stub-runner-amqp-example]]
==== Examples
Assume that you have the following Maven repository with a deployed stubs for the
`spring-cloud-contract-amqp-test` application.
[source,bash,indent=0]
----
└── .m2
└── repository
└── com
└── example
└── spring-cloud-contract-amqp-test
├── 0.4.0-SNAPSHOT
│   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT.pom
│   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume that the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── contracts
└── shouldProduceValidPersonData.groovy
----
Consider the following contract:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=amqp_contract,indent=0]
----
Now consider the following Spring configuration:
[source,yaml]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/resources/application.yml[]
----
[[features-messaging-stub-runner-amqp-triggering]]
===== Triggering the message
To trigger a message using the contract above, use the `StubTrigger` interface as
follows:
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
The message has a destination of `contract-test.exchange`, so the Spring AMQP stub runner
integration looks for bindings related to this exchange.
[source,java]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_binding,indent=0]
----
The binding definition binds the queue `test.queue`. As a result, the following listener
definition is matched and invoked with the contract message.
[source,java]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_listener,indent=0]
----
Also, the following annotated listener matches and is invoked:
[source,java]
----
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/MessageSubscriberRabbitListener.java[tags=amqp_annotated_listener,indent=0]
----
NOTE: The message is directly handed over to the `onMessage` method of the
`MessageListener` associated with the matching `SimpleMessageListenerContainer`.
[[features-messaging-stub-runner-amqp-configuration]]
===== Spring AMQP Test Configuration
In order to avoid Spring AMQP trying to connect to a running broker during our tests
configure a mock `ConnectionFactory`.
To disable the mocked ConnectionFactory, set the following property:
`stubrunner.amqp.mockConnection=false`
[source,yaml]
----
stubrunner:
amqp:
mockConnection: false
----

View File

@@ -1,14 +1,74 @@
:branch: master
:samples_branch: 2.2.x
[[features-stub-runner-features-stub-runner]]
== Spring Cloud Contract Stub Runner
One of the issues that you might encounter while using Spring Cloud Contract Verifier is
passing the generated WireMock JSON stubs from the server side to the client side (or to
various clients). The same takes place in terms of client-side generation for messaging.
Copying the JSON files and setting the client side for messaging manually is out of the
question. That is why we introduced Spring Cloud Contract Stub Runner. It can
automatically download and run the stubs for you.
[[features-stub-runner-features-stub-runner-snapshot-versions]]
=== Snapshot versions
Add the additional snapshot repository to your `build.gradle` file to use snapshot
versions, which are automatically uploaded after every successful build:
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
include::{standalone_samples_path}/http-server/pom.xml[tags=repos,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
include::{standalone_samples_path}/http-server/build.gradle[tags=repos,indent=0]
----
====
[[features-stub-runner-publishing-stubs-as-jars]]
=== Publishing Stubs as JARs
The easiest approach would be to centralize the way stubs are kept. For example, you can
keep them as jars in a Maven repository.
TIP: For both Maven and Gradle, the setup comes ready to work. However, you can customize
it if you want to.
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<!-- First disable the default jar setup in the properties section -->
include::{samples_url}/producer_with_restdocs/pom.xml[tags=skip_jar,indent=0]
<!-- Next add the assembly plugin to your build -->
include::{samples_url}/producer_with_restdocs/pom.xml[tags=assembly,indent=0]
<!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml -->
include::{samples_url}/producer_with_restdocs/src/assembly/stub.xml[indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
include::{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle[tags=jar_setup,indent=0]
----
====
[[features-stub-runner-core]]
=== Stub Runner Core
Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
https://martinfowler.com/articles/consumerDrivenContracts.html[Consumer Driven Contracts].
Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions.
For messaging, special stub routes are defined.
[[features-stub-runner-retrieving]]
==== Retrieving stubs
You can pick the following options of acquiring stubs
@@ -17,8 +77,10 @@ You can pick the following options of acquiring stubs
- Classpath scanning solution that searches classpath via pattern to retrieve stubs
- Write your own implementation of the `org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder` for full customization
// TODO: Fix link
The latter example is described in the <<custom_stub_runner, Custom Stub Runner>> section.
[[features-stub-runner-downloading-stub]]
===== Stub downloading
You can control the stub downloading via the `stubsMode` switch. It picks value from the
@@ -35,6 +97,7 @@ Example:
@AutoConfigureStubRunner(repositoryRoot="https://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)
----
[[features-stub-runner-classpath-scanning]]
===== Classpath scanning
If you set the `stubsMode` property to `StubRunnerProperties.StubsMode.CLASSPATH`
@@ -51,6 +114,7 @@ Let's look at the following example:
If you've added the dependencies to your classpath
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
@@ -92,6 +156,7 @@ testCompile("com.example.foo:bar:1.0.0:superstubs") {
transitive = false
}
----
====
Then the following locations on your classpath will get scanned. For `com.example:beer-api-producer-restdocs`
@@ -146,6 +211,7 @@ structure in your stubs jar.
By maintaining this structure classpath gets scanned and you can profit from the messaging /
HTTP stubs without the need to download artifacts.
[[features-stub-runner-configuring-http-server-stubs]]
===== Configuring HTTP Server Stubs
Stub Runner has a notion of a `HttpServerStub` that abstracts the underlaying
@@ -165,57 +231,22 @@ case might be starting WireMock for the given artifact id, on an HTTPs port. Exa
.WireMockHttpServerStubConfigurer implementation
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=wireMockHttpServerStubConfigurer]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=wireMockHttpServerStubConfigurer]
----
You can then reuse it via the annotation
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=annotation]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=annotation]
----
Whenever an https port is found, it will take precedence over the http one.
[[features-stub-runner-running-stubs]]
==== Running stubs
===== Running using main app
You can set the following options to the main class:
[source,groovy,indent=0]
----
-c, --classifier Suffix for the jar containing stubs (e.
g. 'stubs' if the stub jar would
have a 'stubs' classifier for stubs:
foobar-stubs ). Defaults to 'stubs'
(default: stubs)
--maxPort, --maxp <Integer> Maximum port value to be assigned to
the WireMock instance. Defaults to
15000 (default: 15000)
--minPort, --minp <Integer> Minimum port value to be assigned to
the WireMock instance. Defaults to
10000 (default: 10000)
-p, --password Password to user when connecting to
repository
--phost, --proxyHost Proxy host to use for repository
requests
--pport, --proxyPort [Integer] Proxy port to use for repository
requests
-r, --root Location of a Jar containing server
where you keep your stubs (e.g. http:
//nexus.
net/content/repositories/repository)
-s, --stubs Comma separated list of Ivy
representation of jars with stubs.
Eg. groupid:artifactid1,groupid2:
artifactid2:classifier
--sm, --stubsMode Stubs mode to be used. Acceptable values
[CLASSPATH, LOCAL, REMOTE]
-u, --username Username to user when connecting to
repository
----
[[features-stub-runner-http-stubs]]
===== HTTP Stubs
Stubs are defined in JSON documents, whose syntax is defined in http://wiremock.org/stubbing.html[WireMock documentation]
@@ -239,6 +270,7 @@ Example:
}
----
[[features-stub-runner-viewing]]
===== Viewing registered mappings
Every stubbed collaborator exposes list of defined mappings under `__/admin/` endpoint.
@@ -294,17 +326,19 @@ mappings available for the given server:
]
----
[[features-stub-runner-messaging]]
===== Messaging Stubs
Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.
[[features-stub-runner-junit]]
=== Stub Runner JUnit Rule and Stub Runner JUnit5 Extension
Stub Runner comes with a JUnit rule thanks to which you can very easily download and run stubs for given group and artifact id:
[source,java,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=classrule]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=classrule]
----
There's also a `StubRunnerExtension` available for JUnit 5. `StubRunnerRule` and `StubRunnerExtension` work in a very
@@ -317,74 +351,79 @@ similar fashion. After the rule/ extension is executed, Stub Runner connects to
- feed the WireMock server with all JSON files that are valid WireMock definitions
- can also send messages (remember to pass an implementation of `MessageVerifier` interface)
Stub Runner uses https://wiki.eclipse.org/Aether[Eclipse Aether] mechanism to download the Maven dependencies.
Stub Runner uses https://wiki.eclipse.org/Aether[Eclipse Aether] mechanism to download the Maven dependencies.
Check their https://wiki.eclipse.org/Aether[docs] for more information.
Since the `StubRunnerRule` and `StubRunnerExtension` implement the `StubFinder` they allow you to find the started stubs:
[source,groovy,indent=0]
----
include::src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java[lines=16..-1]
include::{stubrunner_core_path}/src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java[lines=16..-1]
----
Example of usage in Spock tests:
[source,groovy,indent=0]
====
[source,groovy,indent=0,subs="verbatim,attributes",role="primary"]
.spock
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleSpec.groovy[tags=classrule]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleSpec.groovy[tags=classrule]
----
Example of usage in JUnit tests:
[source,java,indent=0]
[source,java,indent=0,subs="verbatim,attributes",role="secondary"]
.junit 4
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=test]
----
JUnit 5 Extension example:
[source,java,indent=0]
[source,java,indent=0,subs="verbatim,attributes",role="secondary"]
.junit 5
----
include::src/test/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerJUnit5ExtensionTests.java[tags=extension]
include::{stubrunner_core_path}/src/test/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerJUnit5ExtensionTests.java[tags=extension]
----
====
//TODO: Fix link
Check the *Common properties for JUnit and Spring* for more information on how to apply global configuration of Stub Runner.
IMPORTANT: To use the JUnit rule or JUnit 5 extension together with messaging, you have to provide an implementation of the
`MessageVerifier` interface to the rule builder (e.g. `rule.messageVerifier(new MyMessageVerifier())`).
If you don't do this, then whenever you try to send a message an exception will be thrown.
[[features-stub-runner-rule-maven-settings]]
==== Maven settings
The stub downloader honors Maven settings for a different local repository folder.
Authentication details for repositories and profiles are currently not taken into account, so you need to specify it using the properties mentioned above.
[[features-stub-runner-rule-fixed-ports]]
==== Providing fixed ports
You can also run your stubs on fixed ports. You can do it in two different ways. One is to pass it in the properties, and the other via fluent API of
JUnit rule.
[[features-stub-runner-rule-fluent-api]]
==== Fluent API
When using the `StubRunnerRule` or `StubRunnerExtension` you can add a stub to download and then pass the port for the last downloaded stub.
[source,java,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=classrule_with_port]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=classrule_with_port]
----
You can see that for this example the following test is valid:
[source,java,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=test_with_port]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleCustomPortJUnitTest.java[tags=test_with_port]
----
[[features-stub-runner-rule-spring]]
==== Stub Runner with Spring
Sets up Spring configuration of the Stub Runner project.
By providing a list of stubs inside your configuration file the Stub Runner automatically downloads
By providing a list of stubs inside your configuration file the Stub Runner automatically downloads
and registers in WireMock the selected stubs.
If you want to find the URL of your stubbed dependency you can autowire the `StubFinder` interface and use
@@ -392,14 +431,14 @@ its methods as presented below:
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=test]
----
for the following configuration file:
[source,yml,indent=0]
----
include::src/test/resources/application-test.yml[tags=test]
include::{stubrunner_core_path}/src/test/resources/application-test.yml[tags=test]
----
Instead of using the properties you can also use the properties inside the `@AutoConfigureStubRunner`.
@@ -407,7 +446,7 @@ Below you can find an example of achieving the same result by setting values on
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=autoconfigure]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=autoconfigure]
----
Stub Runner Spring registers environment variables in the following manner
@@ -433,6 +472,7 @@ int fooPort;
int barPort;
----
[[features-stub-runner-cloud]]
=== Stub Runner Spring Cloud
Stub Runner can integrate with Spring Cloud.
@@ -442,6 +482,7 @@ For real life examples you can check the
- https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/{samples_branch}/producer[producer app sample]
- https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/{samples_branch}/consumer_with_discovery[consumer app sample]
[[features-stub-runner-cloud-stubbing-discovery]]
==== Stubbing Service Discovery
The most important feature of `Stub Runner Spring Cloud` is the fact that it's stubbing
@@ -457,16 +498,17 @@ For example this test will pass
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=test]
----
for the following configuration file
[source,yml,indent=0]
----
include::src/test/resources/application.yml[tags=ids]
include::{stubrunner_core_path}/src/test/resources/application.yml[tags=ids]
----
[[features-stub-runner-cloud-stubbing-profiles]]
===== Test profiles and service discovery
In your integration tests you typically don't want to call neither a discovery service (e.g. Eureka)
@@ -485,6 +527,7 @@ via a static block like presented below (example for Eureka)
}
----
[[features-stub-runner-additional-config]]
==== Additional Configuration
You can match the artifactId of the stub with the name of your app by using the `stubrunner.idsToServiceIds:` map.
@@ -503,6 +546,7 @@ via the following system properties or environment variables
- `org.apache.maven.user-settings` - path to custom maven user settings location
- `org.apache.maven.global-settings` - path to maven global settings location
[[features-stub-runner-boot]]
=== Stub Runner Boot Application
Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to
@@ -512,8 +556,10 @@ One of the use-cases is to run some smoke (end to end) tests on a deployed appli
You can check out the https://github.com/spring-cloud/spring-cloud-pipelines[Spring Cloud Pipelines]
project for more information.
[[features-stub-runner-boot-how]]
==== How to use it?
[[features-stub-runner-boot-server]]
===== Stub Runner Server
Just add the
@@ -527,6 +573,7 @@ Annotate a class with `@EnableStubRunnerServer`, build a fat-jar and you're read
For the properties check the *Stub Runner Spring* section.
[[features-stub-runner-boot-how-fat-jar]]
===== Stub Runner Server Fat Jar
You can download a standalone JAR from Maven (e.g. for version 2.0.1.RELEASE), as follows:
@@ -537,6 +584,7 @@ $ wget -O stub-runner.jar 'https://search.maven.org/remotecontent?filepath=org/s
$ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...
----
[[features-stub-runner-boot-how-cli]]
===== Spring Cloud CLI
Starting from `1.4.0.RELEASE` version of the https://cloud.spring.io/spring-cloud-cli[Spring Cloud CLI]
@@ -558,13 +606,16 @@ stubrunner:
and then just call `spring cloud stubrunner` from your terminal window to start
the Stub Runner server. It will be available at port `8750`.
[[features-stub-runner-boot-endpoints]]
==== Endpoints
[[features-stub-runner-boot-endpoints-http]]
===== HTTP
- GET `/stubs` - returns a list of all running stubs in `ivy:integer` notation
- GET `/stubs/{ivy}` - returns a port for the given `ivy` notation (when calling the endpoint `ivy` can also be `artifactId` only)
[[features-stub-runner-boot-endpoints-messaging]]
===== Messaging
For Messaging
@@ -573,13 +624,15 @@ For Messaging
- POST `/triggers/{label}` - executes a trigger with `label`
- POST `/triggers/{ivy}/{label}` - executes a trigger with `label` for the given `ivy` notation (when calling the endpoint `ivy` can also be `artifactId` only)
[[features-stub-runner-boot-endpoints-example]]
==== Example
[source,groovy,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy[tags=boot_usage]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy[tags=boot_usage]
----
[[features-stub-runner-boot-service-discovery]]
==== Stub Runner Boot with Service Discovery
One of the possibilities of using Stub Runner Boot is to use it as a feed of stubs for "smoke-tests". What does it mean?
@@ -597,7 +650,7 @@ The problem with this approach is such that if you're doing microservices most l
[source,java,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka]
----
As you can see we want to start a Stub Runner Boot server `@EnableStubRunnerServer`, enable Eureka client `@EnableEurekaClient`
@@ -609,7 +662,7 @@ Now let's assume that we want to start this application so that the stubs get au
[source,bash,indent=0]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka_args]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/serverexamples/StubRunnerBootEurekaExample.java[tags=stubrunnereureka_args]
----
That way your deployed application can send requests to started WireMock servers via the service
@@ -617,7 +670,8 @@ discovery. Most likely points 1-3 could be set by default in `application.yml` c
likely to change. That way you can provide only the list of stubs to download whenever you start
the Stub Runner Boot.
=== Stubs Per Consumer
[[features-stub-runner-stubs-per-consumer]]
=== Consumer-Driven Contracts: Stubs Per Consumer
There are cases in which 2 consumers of the same endpoint want to have 2 different responses.
@@ -687,7 +741,7 @@ Or set the test as follows:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy[tags=test]
...
}
----
@@ -699,7 +753,7 @@ Or set the consumer name explicitly
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy[tags=test]
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy[tags=test]
...
}
----
@@ -708,4 +762,63 @@ Then only the stubs registered under a path that contains the `foo-consumer` in
`src/test/resources/contracts/foo-consumer/some/contracts/...` folder) will be allowed to be referenced.
You can check out https://github.com/spring-cloud/spring-cloud-contract/issues/224[issue 224] for more
information about the reasons behind this change.
information about the reasons behind this change.
[[features-stub-runner-common]]
=== Common
This section briefly describes common properties, including:
* <<features-stub-runner-common-properties-junit-spring>>
* <<features-stub-runner-stub-runner-stub-ids>>
[[features-stub-runner-common-properties-junit-spring]]
==== Common Properties for JUnit and Spring
You can set repetitive properties by using system properties or Spring configuration
properties. Here are their names with their default values:
[frame="topbot",options="header"]
|===============
| Property name | Default value | Description
|stubrunner.minPort|10000| Minimum value of a port for a started WireMock with stubs.
|stubrunner.maxPort|15000| Maximum value of a port for a started WireMock with stubs.
|stubrunner.repositoryRoot|| Maven repo URL. If blank, then call the local maven repo.
|stubrunner.classifier|stubs| Default classifier for the stub artifacts.
|stubrunner.stubsMode|CLASSPATH| The way you want to fetch and register the stubs
|stubrunner.ids|| Array of Ivy notation stubs to download.
|stubrunner.username|| Optional username to access the tool that stores the JARs with
stubs.
|stubrunner.password|| Optional password to access the tool that stores the JARs with
stubs.
|stubrunner.stubsPerConsumer|false| Set to `true` if you want to use different stubs for
each consumer instead of registering all stubs for every consumer.
|stubrunner.consumerName|| If you want to use a stub for each consumer and want to
override the consumer name just change this value.
|===============
[[features-stub-runner-stub-runner-stub-ids]]
==== Stub Runner Stubs IDs
You can provide the stubs to download via the `stubrunner.ids` system property. They
follow this pattern:
[source,java,indent=0]
----
groupId:artifactId:version:classifier:port
----
Note that `version`, `classifier` and `port` are optional.
* If you do not provide the `port`, a random one will be picked.
* If you do not provide the `classifier`, the default is used. (Note that you can
pass an empty classifier this way: `groupId:artifactId:version:`).
* If you do not provide the `version`, then the `+` will be passed and the latest one is
downloaded.
`port` means the port of the WireMock server.
IMPORTANT: Starting with version 1.0.4, you can provide a range of versions that you
would like the Stub Runner to take into consideration. You can read more about the
https://wiki.eclipse.org/Aether/New_and_Noteworthy#Version_Ranges[Aether versioning
ranges here].

View File

@@ -0,0 +1,175 @@
[[features-wiremock]]
== Spring Cloud Contract WireMock
include::_attributes.adoc[]
The Spring Cloud Contract WireMock modules let you use https://github.com/tomakehurst/wiremock[WireMock] in a
Spring Boot application. Check out the
https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples[samples]
for more details.
If you have a Spring Boot application that uses Tomcat as an embedded server (which is
the default with `spring-boot-starter-web`), you can add
`spring-cloud-starter-contract-stub-runner` to your classpath and add `@AutoConfigureWireMock` in
order to be able to use Wiremock in your tests. Wiremock runs as a stub server and you
can register stub behavior using a Java API or via static JSON declarations as part of
your test. The following code shows an example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test2]
----
To start the stub server on a different port use (for example),
`@AutoConfigureWireMock(port=9999)`. For a random port, use a value of `0`. The stub
server port can be bound in the test application context with the "wiremock.server.port"
property. Using `@AutoConfigureWireMock` adds a bean of type `WiremockConfiguration` to
your test application context, where it will be cached in between methods and classes
having the same context, the same as for Spring integration tests. Also you can inject a bean of type `WireMockServer` into your test.
[[features-wiremock-registering-stubs]]
=== Registering Stubs Automatically
If you use `@AutoConfigureWireMock`, it registers WireMock JSON stubs from the file
system or classpath (by default, from `file:src/test/resources/mappings`). You can
customize the locations using the `stubs` attribute in the annotation, which can be an
Ant-style resource pattern or a directory. In the case of a directory, `**/*.json` is
appended. The following code shows an example:
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}
----
NOTE: Actually, WireMock always loads mappings from `src/test/resources/mappings` *as
well as* the custom locations in the stubs attribute. To change this behavior, you can
also specify a files root as described in the next section of this document.
If you're using Spring Cloud Contract's default stub jars, then your
stubs are stored under `/META-INF/group-id/artifact-id/versions/mappings/` folder. If you want to register all stubs from that location, from all embedded JARs, then it's enough to use the following syntax.
[source,java,indent=0]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java[tags=load_all_stubs]
----
[[features-wiremock-using-files]]
=== Using Files to Specify the Stub Bodies
WireMock can read response bodies from files on the classpath or the file system. In that
case, you can see in the JSON DSL that the response has a `bodyFileName` instead of a
(literal) `body`. The files are resolved relative to a root directory (by default,
`src/test/resources/\__files`). To customize this location you can set the `files`
attribute in the `@AutoConfigureWireMock` annotation to the location of the parent
directory (in other words, `__files` is a subdirectory). You can use Spring resource
notation to refer to `file:...` or `classpath:...` locations. Generic URLs are not
supported. A list of values can be given, in which case WireMock resolves the first file
that exists when it needs to find a response body.
NOTE: When you configure the `files` root, it also affects the
automatic loading of stubs, because they come from the root location
in a subdirectory called "mappings". The value of `files` has no
effect on the stubs loaded explicitly from the `stubs` attribute.
[[features-wiremock-junit-rule]]
=== Alternative: Using JUnit Rules
For a more conventional WireMock experience, you can use JUnit `@Rules` to start and stop
the server. To do so, use the `WireMockSpring` convenience class to obtain an `Options`
instance, as shown in the following example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test2]
----
The `@ClassRule` means that the server shuts down after all the methods in this class
have been run.
[[features-wiremock-relaxed-ssl]]
=== Relaxed SSL Validation for Rest Template
WireMock lets you stub a "secure" server with an "https" URL protocol. If your
application wants to contact that stub server in an integration test, it will find that
the SSL certificates are not valid (the usual problem with self-installed certificates).
The best option is often to re-configure the client to use "http". If that's not an
option, you can ask Spring to configure an HTTP client that ignores SSL validation errors
(do so only for tests, of course).
To make this work with minimum fuss, you need to be using the Spring Boot
`RestTemplateBuilder` in your app, as shown in the following example:
[source,java,indent=0]
----
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
----
You need `RestTemplateBuilder` because the builder is passed through callbacks to
initialize it, so the SSL validation can be set up in the client at that point. This
happens automatically in your test if you are using the `@AutoConfigureWireMock`
annotation or the stub runner. If you use the JUnit `@Rule` approach, you need to add the
`@AutoConfigureHttpClient` annotation as well, as shown in the following example:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(6443));
...
}
----
If you are using `spring-boot-starter-test`, you have the Apache HTTP client on the
classpath and it is selected by the `RestTemplateBuilder` and configured to ignore SSL
errors. If you use the default `java.net` client, you do not need the annotation (but it
won't do any harm). There is no support currently for other clients, but it may be added
in future releases.
To disable the custom `RestTemplateBuilder`, set the `wiremock.rest-template-ssl-enabled`
property to `false`.
[[features-wiremock-spring-mvc-mocks]]
=== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into
a Spring `MockRestServiceServer`. The following code shows an example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java[tags=wiremock_test]
----
The `baseUrl` value is prepended to all mock calls, and the `stubs()` method takes a stub
path resource pattern as an argument. In the preceding example, the stub defined at
`/stubs/resource.json` is loaded into the mock server. If the `RestTemplate` is asked to
visit `https://example.org/`, it gets the responses as being declared at that URL. More
than one stub pattern can be specified, and each one can be a directory (for a recursive
list of all ".json"), a fixed filename (as in the example above), or an Ant-style
pattern. The JSON format is the normal WireMock format, which you can read about in the
https://wiremock.org/docs/stubbing/[WireMock website].
Currently, the Spring Cloud Contract Verifier supports Tomcat, Jetty, and Undertow as
Spring Boot embedded servers, and Wiremock itself has "native" support for a particular
version of Jetty (currently 9.2). To use the native Jetty, you need to add the native
Wiremock dependencies and exclude the Spring Boot container (if there is one).

View File

@@ -1,13 +0,0 @@
include::_verifier_introduction.adoc[]
include::_verifier_faq.adoc[]
include::_verifier_setup.adoc[]
include::_verifier_messaging.adoc[]
include::_verifier_stubrunner.adoc[]
include::_verifier_stubrunner_msg.adoc[]
include::_verifier_contract.adoc[]

View File

@@ -1,422 +0,0 @@
:core_path: ../../..
:doc_samples: {core_path}/samples/wiremock-jetty
:wiremock_tests: {core_path}/spring-cloud-contract-wiremock
== Spring Cloud Contract WireMock
The Spring Cloud Contract WireMock modules let you use https://github.com/tomakehurst/wiremock[WireMock] in a
Spring Boot application. Check out the
https://github.com/spring-cloud/spring-cloud-contract/tree/{branch}/samples[samples]
for more details.
If you have a Spring Boot application that uses Tomcat as an embedded server (which is
the default with `spring-boot-starter-web`), you can add
`spring-cloud-starter-contract-stub-runner` to your classpath and add `@AutoConfigureWireMock` in
order to be able to use Wiremock in your tests. Wiremock runs as a stub server and you
can register stub behavior using a Java API or via static JSON declarations as part of
your test. The following code shows an example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsTests.java[tags=wiremock_test2]
----
To start the stub server on a different port use (for example),
`@AutoConfigureWireMock(port=9999)`. For a random port, use a value of `0`. The stub
server port can be bound in the test application context with the "wiremock.server.port"
property. Using `@AutoConfigureWireMock` adds a bean of type `WiremockConfiguration` to
your test application context, where it will be cached in between methods and classes
having the same context, the same as for Spring integration tests. Also you can inject a bean of type `WireMockServer` into your test.
=== Registering Stubs Automatically
If you use `@AutoConfigureWireMock`, it registers WireMock JSON stubs from the file
system or classpath (by default, from `file:src/test/resources/mappings`). You can
customize the locations using the `stubs` attribute in the annotation, which can be an
Ant-style resource pattern or a directory. In the case of a directory, `**/*.json` is
appended. The following code shows an example:
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}
----
NOTE: Actually, WireMock always loads mappings from `src/test/resources/mappings` *as
well as* the custom locations in the stubs attribute. To change this behavior, you can
also specify a files root as described in the next section of this document.
If you're using Spring Cloud Contract's default stub jars, then your
stubs are stored under `/META-INF/group-id/artifact-id/versions/mappings/` folder. If you want to register all stubs from that location, from all embedded JARs, then it's enough to use the following syntax.
[source,java,indent=0]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java[tags=load_all_stubs]
----
=== Using Files to Specify the Stub Bodies
WireMock can read response bodies from files on the classpath or the file system. In that
case, you can see in the JSON DSL that the response has a `bodyFileName` instead of a
(literal) `body`. The files are resolved relative to a root directory (by default,
`src/test/resources/\__files`). To customize this location you can set the `files`
attribute in the `@AutoConfigureWireMock` annotation to the location of the parent
directory (in other words, `__files` is a subdirectory). You can use Spring resource
notation to refer to `file:...` or `classpath:...` locations. Generic URLs are not
supported. A list of values can be given, in which case WireMock resolves the first file
that exists when it needs to find a response body.
NOTE: When you configure the `files` root, it also affects the
automatic loading of stubs, because they come from the root location
in a subdirectory called "mappings". The value of `files` has no
effect on the stubs loaded explicitly from the `stubs` attribute.
=== Alternative: Using JUnit Rules
For a more conventional WireMock experience, you can use JUnit `@Rules` to start and stop
the server. To do so, use the `WireMockSpring` convenience class to obtain an `Options`
instance, as shown in the following example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test1]
include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.java[tags=wiremock_test2]
----
The `@ClassRule` means that the server shuts down after all the methods in this class
have been run.
=== Relaxed SSL Validation for Rest Template
WireMock lets you stub a "secure" server with an "https" URL protocol. If your
application wants to contact that stub server in an integration test, it will find that
the SSL certificates are not valid (the usual problem with self-installed certificates).
The best option is often to re-configure the client to use "http". If that's not an
option, you can ask Spring to configure an HTTP client that ignores SSL validation errors
(do so only for tests, of course).
To make this work with minimum fuss, you need to be using the Spring Boot
`RestTemplateBuilder` in your app, as shown in the following example:
[source,java,indent=0]
----
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
----
You need `RestTemplateBuilder` because the builder is passed through callbacks to
initialize it, so the SSL validation can be set up in the client at that point. This
happens automatically in your test if you are using the `@AutoConfigureWireMock`
annotation or the stub runner. If you use the JUnit `@Rule` approach, you need to add the
`@AutoConfigureHttpClient` annotation as well, as shown in the following example:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {
@ClassRule
public static WireMockClassRule wiremock = new WireMockClassRule(
WireMockSpring.options().httpsPort(6443));
...
}
----
If you are using `spring-boot-starter-test`, you have the Apache HTTP client on the
classpath and it is selected by the `RestTemplateBuilder` and configured to ignore SSL
errors. If you use the default `java.net` client, you do not need the annotation (but it
won't do any harm). There is no support currently for other clients, but it may be added
in future releases.
To disable the custom `RestTemplateBuilder`, set the `wiremock.rest-template-ssl-enabled`
property to `false`.
=== WireMock and Spring MVC Mocks
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into
a Spring `MockRestServiceServer`. The following code shows an example:
[source,java,indent=0]
----
include::{doc_samples}/src/test/java/com/example/WiremockForDocsMockServerApplicationTests.java[tags=wiremock_test]
----
The `baseUrl` value is prepended to all mock calls, and the `stubs()` method takes a stub
path resource pattern as an argument. In the preceding example, the stub defined at
`/stubs/resource.json` is loaded into the mock server. If the `RestTemplate` is asked to
visit `https://example.org/`, it gets the responses as being declared at that URL. More
than one stub pattern can be specified, and each one can be a directory (for a recursive
list of all ".json"), a fixed filename (as in the example above), or an Ant-style
pattern. The JSON format is the normal WireMock format, which you can read about in the
https://wiremock.org/docs/stubbing/[WireMock website].
Currently, the Spring Cloud Contract Verifier supports Tomcat, Jetty, and Undertow as
Spring Boot embedded servers, and Wiremock itself has "native" support for a particular
version of Jetty (currently 9.2). To use the native Jetty, you need to add the native
Wiremock dependencies and exclude the Spring Boot container (if there is one).
=== Customization of WireMock configuration
You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type
in order to customize the WireMock configuration (e.g. add custom transformers).
Example:
[source,java,indent=0]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_1]
// perform your customization here
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_2]
----
=== Generating Stubs using REST Docs
https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
or `WebTestClient` or Rest Assured. At the same time that you generate documentation for your API, you can also
generate WireMock stubs by using Spring Cloud Contract WireMock. To do so, write your
normal REST Docs test cases and use `@AutoConfigureRestDocs` to have stubs be
automatically generated in the REST Docs output directory.
[plantuml, rest-docs, png]
----
"API Producer"->"API Producer": add Spring Cloud Contract (SCC) \nStub Runner dependency
"API Producer"->"API Producer": setup stub jar assembly
"API Producer"->"API Producer": write and setup REST Docs tests
"API Producer"->"Build": run build
"Build"->"REST Docs": generate API \ndocumentation
"REST Docs"->"SCC": generate stubs from the \nREST Docs tests
"REST Docs"->"SCC": generate contracts from the \nREST Docs tests
"Build"->"Build": assemble stubs jar with \nstubs and contracts
"Build"->"Nexus / Artifactory": upload contracts \nand stubs and the project arifact
"Build"->"API Producer": Build successful
"API Consumer"->"API Consumer": add SCC Stub Runner \ndependency
"API Consumer"->"API Consumer": write a SCC Stub Runner \nbased contract test
"SCC Stub Runner"->"Nexus / Artifactory": test asks for [API Producer] stubs
"Nexus / Artifactory"->"SCC Stub Runner": fetch the [API Producer] stubs
"SCC Stub Runner"->"SCC Stub Runner": run in memory\n HTTP server stubs
"API Consumer"->"SCC Stub Runner": send a request \nto the HTTP server stub
"SCC Stub Runner"->"API Consumer": communication is correct
----
The following code shows an
example using `MockMvc`:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(get("/resource"))
.andExpect(content().string("Hello World"))
.andDo(document("resource"));
}
}
----
This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches
all GET requests to the "/resource" path. The same example with `WebTestClient` (used
for testing Spring WebFlux applications) would look like this:
[source,java,indent=0]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureWebTestClient
public class ApplicationTests {
@Autowired
private WebTestClient client;
@Test
public void contextLoads() throws Exception {
client.get().uri("/resource").exchange()
.expectBody(String.class).isEqualTo("Hello World")
.consumeWith(document("resource"));
}
}
----
Without any additional configuration, these tests create a stub with a request matcher
for the HTTP method and all headers except "host" and "content-length". To match the
request more precisely (for example, to match the body of a POST or PUT), we need to
explicitly create a request matcher. Doing so has two effects:
* Creating a stub that matches only in the way you specify.
* Asserting that the request in the test case also matches the same conditions.
The main entry point for this feature is `WireMockRestDocs.verify()`, which can be used
as a substitute for the `document()` convenience method, as shown in the following
example:
[source,java,indent=0]
import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
----
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify().jsonPath("$.id")
.stub("resource"));
}
}
----
This contract specifies that any valid POST with an "id" field receives the response
defined in this test. You can chain together calls to `.jsonPath()` to add additional
matchers. If JSON Path is unfamiliar, The https://github.com/jayway/JsonPath[JayWay
documentation] can help you get up to speed. The `WebTestClient` version of this test
has a similar `verify()` static helper that you insert in the same place.
Instead of the `jsonPath` and `contentType` convenience methods, you can also use the
WireMock APIs to verify that the request matches the created stub, as shown in the
following example:
[source,java,indent=0]
----
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify()
.wiremock(WireMock.post(
urlPathEquals("/resource"))
.withRequestBody(matchingJsonPath("$.id"))
.stub("post-resource"));
}
----
The WireMock API is rich. You can match headers, query parameters, and request body by
regex as well as by JSON path. These features can be used to create stubs with a wider
range of parameters. The above example generates a stub resembling the following example:
.post-resource.json
[source,json]
----
{
"request" : {
"url" : "/resource",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$.id"
}]
},
"response" : {
"status" : 200,
"body" : "Hello World",
"headers" : {
"X-Application-Context" : "application:-1",
"Content-Type" : "text/plain"
}
}
}
----
NOTE: You can use either the `wiremock()` method or the `jsonPath()` and `contentType()`
methods to create request matchers, but you can't use both approaches.
On the consumer side, you can make the `resource.json` generated earlier in this section
available on the classpath (by
<<publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a
number of different ways, including by using
`@AutoConfigureWireMock(stubs="classpath:resource.json")`, as described earlier in this
document.
=== Generating Contracts by Using REST Docs
You can also generate Spring Cloud Contract DSL files and documentation with Spring REST
Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts
and the stubs.
Why would you want to use this feature? Some people in the community asked questions
about a situation in which they would like to move to DSL-based contract definition,
but they already have a lot of Spring MVC tests. Using this feature lets you generate
the contract files that you can later modify and move to folders (defined in your
configuration) so that the plugin finds them.
TIP: You might wonder why this functionality is in the WireMock module. The functionality
is there because it makes sense to generate both the contracts and the stubs.
Consider the following test:
[source,java]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/ContractDslSnippetTests.java[tags=contract_snippet]
----
The preceding test creates the stub presented in the previous section, generating both
the contract and a documentation file.
The contract is called `index.groovy` and might look like the following example:
[source,groovy]
----
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'POST'
url '/foo'
body('''
{"foo": 23 }
''')
headers {
header('''Accept''', '''application/json''')
header('''Content-Type''', '''application/json''')
}
}
response {
status OK()
body('''
bar
''')
headers {
header('''Content-Type''', '''application/json;charset=UTF-8''')
header('''Content-Length''', '''3''')
}
testMatchers {
jsonPath('$[?(@.foo >= 20)]', byType())
}
}
}
----
The generated document (formatted in Asciidoc in this case) contains a formatted
contract. The location of this file would be `index/dsl-contract.adoc`.

View File

@@ -1,120 +0,0 @@
== Spring Cloud Contract Introduction
Spring Cloud Contract moves TDD to the level of software architecture. It allows to perform consumer, consumer-driven and producer-driven contract testing.
=== History
Before becoming Spring Cloud Contract, this project was called https://github.com/Codearte/accurest[Accurest].
It was created by https://twitter.com/mgrzejszczak[Marcin Grzejszczak] and https://twitter.com/jkubrynski[Jakub Kubrynski]
from (https://github.com/Codearte[Codearte].
The `0.1.0` release took place on 26 Jan 2015 and it became stable with `1.0.0` release on 29 Feb 2016.
=== Why a Contract Verifier?
Assume that we have a system consisting of multiple microservices:
image::{github-raw}/docs/src/main/asciidoc/images/Deps.png[Microservices Architecture]
==== Testing issues
If we wanted to test the application in top left corner to determine whether it can
communicate with other services, we could do one of two things:
- Deploy all microservices and perform end-to-end tests.
- Mock other microservices in unit/integration tests.
Both have their advantages but also a lot of disadvantages.
*Deploy all microservices and perform end to end tests*
Advantages:
- Simulates production.
- Tests real communication between services.
Disadvantages:
- To test one microservice, we have to deploy 6 microservices, a couple of databases,
etc.
- The environment where the tests run is locked for a single suite of tests (nobody else
would be able to run the tests in the meantime).
- They take a long time to run.
- The feedback comes very late in the process.
- They are extremely hard to debug.
*Mock other microservices in unit/integration tests*
Advantages:
- They provide very fast feedback.
- They have no infrastructure requirements.
Disadvantages:
- The implementor of the service creates stubs that might have nothing to do with
reality.
- You can go to production with passing tests and failing production.
To solve the aforementioned issues, Spring Cloud Contract was created. The main idea is to give you very fast feedback, without the need to set up the
whole world of microservices. If you work on stubs, then the only applications you need
are those that your application directly uses.
image::{github-raw}/docs/src/main/asciidoc/images/Stubs2.png[Stubbed Services]
Spring Cloud Contract gives you the certainty that the stubs that you use were
created by the service that you're calling. Also, if you can use them, it means that they
were tested against the producer's side. In short, you can trust those stubs.
=== Purposes
The main purposes of Spring Cloud Contract are:
- To ensure that Http / Messaging stubs (used when developing the client) do exactly
what the actual server-side implementation does.
- To promote ATDD (acceptance test driven developement) method and Microservices architectural style.
- To provide a way to publish changes in contracts that are immediately visible on both
sides.
- To generate boilerplate test code to be used on the server side.
By default Spring Cloud Contract integrates with http://wiremock.org[Wiremock] as the HTTP server stub.
IMPORTANT: Spring Cloud Contract's purpose is NOT to start writing business
features in the contracts. Assume that we have a business use case of fraud check. If a
user can be a fraud for 100 different reasons, we would assume that you would create 2
contracts, one for the positive case and one for the negative case. Contract tests are
used to test contracts between applications and not to simulate full behavior.
include::_verifier_how_it_works.adoc[]
=== Dependencies
The best way to add dependencies is to use the proper `starter` dependency.
For `stub-runner`, use `spring-cloud-starter-stub-runner`. When you use a plugin, add
`spring-cloud-starter-contract-verifier`.
=== Additional Links
Here are some resources related to Spring Cloud Contract Verifier and Stub Runner. Note
that some may be outdated, because the Spring Cloud Contract Verifier project is under
constant development.
==== Spring Cloud Contract video
You can check out the video from the Warsaw JUG about Spring Cloud Contract:
video::sAAklvxmPmk[youtube,start=538,width=640,height=480]
==== Readings
- https://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura[Slides from Marcin Grzejszczak's talk about Accurest]
- https://toomuchcoding.com/blog/categories/accurest/[Accurest related articles from Marcin Grzejszczak's blog]
- https://toomuchcoding.com/blog/categories/spring-cloud-contract/[Spring Cloud Contract related articles from Marcin Grzejszczak's blog]
- https://groovy-lang.org/json.html[Groovy docs regarding JSON]
=== Samples
You can find some samples at
https://github.com/spring-cloud-samples/spring-cloud-contract-samples[samples].

View File

@@ -1,195 +0,0 @@
== Spring Cloud Contract Verifier Messaging
Spring Cloud Contract Verifier lets you verify applications that use messaging as a
means of communication. All of the integrations shown in this document work with Spring,
but you can also create one of your own and use that.
=== Integrations
You can use one of the following four integration configurations:
* Apache Camel
* Spring Integration
* Spring Cloud Stream
* Spring AMQP
Since we use Spring Boot, if you have added one of these libraries to the classpath, all
the messaging configuration is automatically set up.
IMPORTANT: Remember to put `@AutoConfigureMessageVerifier` on the base class of your
generated tests. Otherwise, messaging part of Spring Cloud Contract Verifier does not
work.
IMPORTANT: If you want to use Spring Cloud Stream, remember to add a dependency on
`org.springframework.cloud:spring-cloud-stream-test-support`, as shown here:
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
testCompile "org.springframework.cloud:spring-cloud-stream-test-support"
----
=== Manual Integration Testing
The main interface used by the tests is
`org.springframework.cloud.contract.verifier.messaging.MessageVerifier`.
It defines how to send and receive messages. You can create your own implementation to
achieve the same goal.
In a test, you can inject a `ContractVerifierMessageExchange` to send and receive
messages that follow the contract. Then add `@AutoConfigureMessageVerifier` to your test.
Here's an example:
[source,java,indent=0]
----
@RunWith(SpringTestRunner.class)
@SpringBootTest
@AutoConfigureMessageVerifier
public static class MessagingContractTests {
@Autowired
private MessageVerifier verifier;
...
}
----
NOTE: If your tests require stubs as well, then `@AutoConfigureStubRunner` includes the
messaging configuration, so you only need the one annotation.
=== Publisher-Side Test Generation
Having the `input` or `outputMessage` sections in your DSL results in creation of tests
on the publisher's side. By default, JUnit 4 tests are created. However, there is also a
possibility to create JUnit 5, TestNG or Spock tests.
There are 3 main scenarios that we should take into consideration:
* Scenario 1: There is no input message that produces an output message. The output
message is triggered by a component inside the application (for example, scheduler).
* Scenario 2: The input message triggers an output message.
* Scenario 3: The input message is consumed and there is no output message.
IMPORTANT: The destination passed to `messageFrom` or `sentTo` can have different
meanings for different messaging implementations. For *Stream* and *Integration* it is
first resolved as a `destination` of a channel. Then, if there is no such `destination`
it is resolved as a channel name. For *Camel*, that's a certain component (for example,
`jms`).
==== Scenario 1: No Input Message
For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario1.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_junit_test]
----
And the following Spock test would be created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_test]
----
==== Scenario 2: Output Triggered by Input
For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario2.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_junit]
----
And the following Spock test would be created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_spock]
----
==== Scenario 3: No Output Message
For the given contract:
.Groovy DSL
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl]
----
.YAML
[source,yml,indent=0]
----
include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0]
----
The following JUnit test is created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_junit]
----
And the following Spock test would be created:
[source,groovy]
----
include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_spock]
----
=== Consumer Stub Generation
Unlike the HTTP part, in messaging, we need to publish the Groovy DSL inside the JAR with
a stub. Then it is parsed on the consumer side and proper stubbed routes are created.
For more information, see <<stub-runner-for-messaging>> section.
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
include::{standalone_messaging_samples_path}/stream-sink/pom.xml[tags=jars,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
include::{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle[tags=jar_setup,indent=0]
----

View File

@@ -1,7 +0,0 @@
== Spring Cloud Contract Verifier Setup
You can set up Spring Cloud Contract Verifier in the following ways:
* link:gradle-project.html[As a Gradle project]
* link:maven-project.html[As a Maven project]
* link:docker-project.html[As a Docker project]

View File

@@ -1,182 +0,0 @@
== Spring Cloud Contract Stub Runner
One of the issues that you might encounter while using Spring Cloud Contract Verifier is
passing the generated WireMock JSON stubs from the server side to the client side (or to
various clients). The same takes place in terms of client-side generation for messaging.
Copying the JSON files and setting the client side for messaging manually is out of the
question. That is why we introduced Spring Cloud Contract Stub Runner. It can
automatically download and run the stubs for you.
=== Snapshot versions
Add the additional snapshot repository to your `build.gradle` file to use snapshot
versions, which are automatically uploaded after every successful build:
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
include::{standalone_samples_path}/http-server/pom.xml[tags=repos,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
include::{standalone_samples_path}/http-server/build.gradle[tags=repos,indent=0]
----
[[publishing-stubs-as-jars]]
=== Publishing Stubs as JARs
The easiest approach would be to centralize the way stubs are kept. For example, you can
keep them as jars in a Maven repository.
TIP: For both Maven and Gradle, the setup comes ready to work. However, you can customize
it if you want to.
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<!-- First disable the default jar setup in the properties section -->
include::{samples_url}/producer_with_restdocs/pom.xml[tags=skip_jar,indent=0]
<!-- Next add the assembly plugin to your build -->
include::{samples_url}/producer_with_restdocs/pom.xml[tags=assembly,indent=0]
<!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml -->
include::{samples_url}/producer_with_restdocs/src/assembly/stub.xml[indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
include::{plugins_path}/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle[tags=jar_setup,indent=0]
----
include::{stubrunner_core_path}/README.adoc[]
=== Common
This section briefly describes common properties, including:
* <<common-properties-junit-spring>>
* <<stub-runner-stub-ids>>
[[common-properties-junit-spring]]
==== Common Properties for JUnit and Spring
You can set repetitive properties by using system properties or Spring configuration
properties. Here are their names with their default values:
[frame="topbot",options="header"]
|===============
| Property name | Default value | Description
|stubrunner.minPort|10000| Minimum value of a port for a started WireMock with stubs.
|stubrunner.maxPort|15000| Maximum value of a port for a started WireMock with stubs.
|stubrunner.repositoryRoot|| Maven repo URL. If blank, then call the local maven repo.
|stubrunner.classifier|stubs| Default classifier for the stub artifacts.
|stubrunner.stubsMode|CLASSPATH| The way you want to fetch and register the stubs
|stubrunner.ids|| Array of Ivy notation stubs to download.
|stubrunner.username|| Optional username to access the tool that stores the JARs with
stubs.
|stubrunner.password|| Optional password to access the tool that stores the JARs with
stubs.
|stubrunner.stubsPerConsumer|false| Set to `true` if you want to use different stubs for
each consumer instead of registering all stubs for every consumer.
|stubrunner.consumerName|| If you want to use a stub for each consumer and want to
override the consumer name just change this value.
|===============
[[stub-runner-stub-ids]]
==== Stub Runner Stubs IDs
You can provide the stubs to download via the `stubrunner.ids` system property. They
follow this pattern:
[source,java,indent=0]
----
groupId:artifactId:version:classifier:port
----
Note that `version`, `classifier` and `port` are optional.
* If you do not provide the `port`, a random one will be picked.
* If you do not provide the `classifier`, the default is used. (Note that you can
pass an empty classifier this way: `groupId:artifactId:version:`).
* If you do not provide the `version`, then the `+` will be passed and the latest one is
downloaded.
`port` means the port of the WireMock server.
IMPORTANT: Starting with version 1.0.4, you can provide a range of versions that you
would like the Stub Runner to take into consideration. You can read more about the
https://wiki.eclipse.org/Aether/New_and_Noteworthy#Version_Ranges[Aether versioning
ranges here].
[[stubrunner-docker]]
=== Stub Runner Docker
We're publishing a `spring-cloud/spring-cloud-contract-stub-runner` Docker image
that will start the standalone version of Stub Runner.
If you want to learn more about the basics of Maven, artifact ids,
group ids, classifiers and Artifact Managers, just click here <<docker-project>>.
==== How to use it
Just execute the docker image. You can pass any of the <<common-properties-junit-spring>>
as environment variables. The convention is that all the
letters should be upper case. The camel case notation should
and the dot (`.`) should be separated via underscore (`_`). E.g.
the `stubrunner.repositoryRoot` property should be represented
as a `STUBRUNNER_REPOSITORY_ROOT` environment variable.
==== Example of client side usage in a non JVM project
We'd like to use the stubs created in this <<docker-server-side>> step.
Let's assume that we want to run the stubs on port `9876`. The NodeJS code
is available here:
```bash
$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore
```
Let's run the Stub Runner Boot application with the stubs.
```bash
# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"
```
What's happening is that
- a standalone Stub Runner application got started
- it downloaded the stub with coordinates `com.example:bookstore:0.0.1.RELEASE:stubs` on port `9876`
- it got downloaded from Artifactory running at `http://192.168.0.100:8081/artifactory/libs-release-local`
- after a while Stub Runner will be running on port `8083`
- and the stubs will be running at port `9876`
On the server side we built a stateful stub. Let's use curl to assert
that the stubs are setup properly.
```bash
# let's execute the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
```
IMPORTANT: If you want use the stubs that you have built locally, on your host,
then you should pass the environment variable `-e STUBRUNNER_STUBS_MODE=LOCAL` and mount
the volume of your local m2 `-v "${HOME}/.m2/:/root/.m2:ro"`

View File

@@ -1,75 +0,0 @@
[[stub-runner-for-messaging]]
== Stub Runner for Messaging
Stub Runner can run the published stubs in memory. It can integrate with the following
frameworks:
* Spring Integration
* Spring Cloud Stream
* Apache Camel
* Spring AMQP
It also provides entry points to integrate with any other solution on the market.
IMPORTANT: If you have multiple frameworks on the classpath Stub Runner will need to
define which one should be used. Let's assume that you have both AMQP, Spring Cloud Stream and Spring Integration
on the classpath. Then you need to set `stubrunner.stream.enabled=false` and `stubrunner.integration.enabled=false`.
That way the only remaining framework is Spring AMQP.
=== Stub triggering
To trigger a message, use the `StubTrigger` interface:
[source,groovy]
----
include::{stubrunner_core_path}/src/main/java/org/springframework/cloud/contract/stubrunner/StubTrigger.java[lines=16..-1]
----
For convenience, the `StubFinder` interface extends `StubTrigger`, so you only need one
or the other in your tests.
`StubTrigger` gives you the following options to trigger a message:
* <<trigger-label>>
* <<trigger-group-artifact-ids>>
* <<trigger-artifact-ids>>
* <<trigger-all-messages>>
[[trigger-label]]
==== Trigger by Label
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
[[trigger-group-artifact-ids]]
==== Trigger by Group and Artifact Ids
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_group_artifact,indent=0]
----
[[trigger-artifact-ids]]
==== Trigger by Artifact Ids
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_artifact,indent=0]
----
[[trigger-all-messages]]
==== Trigger All Messages
[source,groovy]
----
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_all,indent=0]
----
include::{tests_path}/spring-cloud-contract-stub-runner-camel/README.adoc[]
include::{tests_path}/spring-cloud-contract-stub-runner-integration/README.adoc[]
include::{tests_path}/spring-cloud-contract-stub-runner-stream/README.adoc[]
include::{tests_path}/spring-cloud-contract-stub-runner-amqp/README.adoc[]

View File

@@ -0,0 +1,373 @@
[[contract-customization]]
= Spring Cloud Contract customization
include::_attributes.adoc[]
In this section we describe how to customize various parts of Spring Cloud Contract.
[[customization-customization]]
== DSL 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.
[[customization-extending]]
=== 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].
[[customization-extending-common-jar]]
=== 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[]
----
[[customization-test-dep]]
=== Add a test 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]
----
==
[[customization-plugin-dep]]
=== Add a test 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]
----
==
[[customization-referencing]]
=== 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]
----
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.
[[customization-wiremock]]
== WireMock customization
In this section you will learn how to customize the way you work with https://wiremock.org[WireMock].
[[customization-wiremock-extension]]
=== 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.
[[customization-wiremock-configuration]]
=== Customization of WireMock configuration
You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type
in order to customize the WireMock configuration (e.g. add custom transformers).
Example:
[source,java,indent=0]
----
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_1]
// perform your customization here
include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_2]
----
[[customization-pluggable-architecture]]
== 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).
[[customization-custom-contract-converter]]
=== 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,java]
----
include::{contract_spec_path}/src/main/java/org/springframework/cloud/contract/spec/ContractConverter.java[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
----
[[customization-custom-test-generator]]
=== 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.java[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
----
[[customization-custom-stub-generator]]
=== 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.
[[customization-custom-stub-runner]]
=== 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.
[[customization-custom-stub-downloader]]
=== 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`
|====

View File

@@ -1,3 +1,4 @@
[[docker]]
= Docker Project
include::_attributes.adoc[]
@@ -8,6 +9,10 @@ against a running application.
TIP: The `EXPLICIT` mode means that the tests generated from contracts will send
real requests and not the mocked ones.
We're also publishing a `spring-cloud/spring-cloud-contract-stub-runner` Docker image
that will start the standalone version of Stub Runner.
[[docker-intro]]
== Short intro to Maven, JARs and Binary storage
Since the Docker image can be used by non JVM projects, it's good to
@@ -48,7 +53,8 @@ and for Docker those would be Docker images. You can store those artifacts
in a manager. Examples of such managers can be https://jfrog.com/artifactory/[Artifactory]
or https://www.sonatype.org/nexus/[Nexus].
== How it works
[[docker-how-it-works]]
== Generating tests on the producer side
The image searches for contracts under the `/contracts` folder.
The output from running the tests will be available under
@@ -63,6 +69,7 @@ It's enough for you to mount your contracts, pass the environment variables
- generate the https://github.com/tomakehurst/wiremock[WireMock] stubs
- (optional - turned on by default) publish the stubs to a Artifact Manager
[[docker-env-vars]]
=== Environment Variables
The Docker image requires some environment variables to point to
@@ -94,12 +101,11 @@ requires authentication, defaults to `REPO_WITH_BINARIES_USERNAME`. If that's no
requires authentication, defaults to `REPO_WITH_BINARIES_PASSWORD`. If that's not set defaults to `password`.
- `EXTERNAL_CONTRACTS_PATH` - path to contracts for the given project, inside the project with contracts.
Defaults to slash separated `EXTERNAL_CONTRACTS_GROUP_ID` concatenated with `/` and `EXTERNAL_CONTRACTS_ARTIFACT_ID`. E.g.
for group id `foo.bar` and artifact id `baz`, would result in `foo/bar/baz` contracts path.
for group id `foo-server-side.bar` and artifact id `baz`, would result in `foo/bar/baz` contracts path.
- `EXTERNAL_CONTRACTS_WORK_OFFLINE` - if set to `true` then will retrieve artifact with contracts
from the container's `.m2`. Mount your local `.m2` as a volume available at the container's `/root/.m2` path.
You must not set both `EXTERNAL_CONTRACTS_WORK_OFFLINE` and `EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL`.
These environment variables are used when tests are executed:
- `APPLICATION_BASE_URL` - url against which tests should be executed.
@@ -108,7 +114,8 @@ will not work)
- `APPLICATION_USERNAME` - (optional) username for basic authentication to your application
- `APPLICATION_PASSWORD` - (optional) password for basic authentication to your application
== Example of usage
[[docker-example-of-usage]]
=== Example of usage
Let's take a look at a simple MVC application
@@ -119,9 +126,6 @@ $ cd bookstore
The contracts are available under `/contracts` folder.
[[docker-server-side]]
== Server side (nodejs)
Since we want to run tests, we could just execute:
```bash
@@ -178,5 +182,72 @@ will be executed against the running application
under http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ .
The stubs will be here http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar.
// TODO: Fix the link
To see how the client side looks like check out the <<stubrunner-docker>> section.
[[docker-stubrunner]]
== Running stubs on the consumer side
This section describes hot to use Docker on the consumer side to fetch and run stubs.
We're publishing a `spring-cloud/spring-cloud-contract-stub-runner` Docker image
that will start the standalone version of Stub Runner.
[[docker-stubrunner-env-vars]]
=== Environment variables
//TODO: Fix the link
Just execute the docker image. You can pass any of the <<common-properties-junit-spring>>
as environment variables. The convention is that all the
letters should be upper case. The camel case notation should
and the dot (`.`) should be separated via underscore (`_`). E.g.
the `stubrunner.repositoryRoot` property should be represented
as a `STUBRUNNER_REPOSITORY_ROOT` environment variable.
[[docker-stubrunner-example]]
=== Example of usage
We'd like to use the stubs created in this <<docker-server-side>> step.
Let's assume that we want to run the stubs on port `9876`. The NodeJS code
is available here:
```bash
$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore
```
Let's run the Stub Runner Boot application with the stubs.
```bash
# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run --rm -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" -e "STUBRUNNER_STUBS_MODE=REMOTE" -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" -p "9876:9876" springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"
```
What's happening is that
- a standalone Stub Runner application got started
- it downloaded the stub with coordinates `com.example:bookstore:0.0.1.RELEASE:stubs` on port `9876`
- it got downloaded from Artifactory running at `http://192.168.0.100:8081/artifactory/libs-release-local`
- after a while Stub Runner will be running on port `8083`
- and the stubs will be running at port `9876`
On the server side we built a stateful stub. Let's use curl to assert
that the stubs are setup properly.
```bash
# let's execute the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
```
IMPORTANT: If you want use the stubs that you have built locally, on your host,
then you should pass the environment variable `-e STUBRUNNER_STUBS_MODE=LOCAL` and mount
the volume of your local m2 `-v "${HOME}/.m2/:/root/.m2:ro"`

View File

@@ -81,9 +81,7 @@ Need more details about {project-full-name}'s core features?
* *Core Features:*
<<{project-name}-features.adoc#contract-features-spring-application, SpringApplication>> |
<<{project-name}-features.adoc#contract-features-external-config, External Configuration>> |
<<{project-name}-features.adoc#contract-features-profiles, Profiles>> |
<<{project-name}-features.adoc#contract-features-logging, Logging>>
<<{project-name}-features.adoc#contract-features-external-config, External Configuration>>
* *Build tools:*
link:maven-project.html[Maven] |
link:gradle-project.html[Gradle] |
@@ -94,12 +92,22 @@ link:docker-project.html[Docker]
== Advanced Topics
Finally, we have a few topics for more advanced users:
* *{project-full-name} Applications Deployment:*
<<deployment.adoc#cloud-deployment, Cloud Deployment>> |
<<deployment.adoc#deployment-service, OS Service>>
* *Build tool plugins:*
<<build-tool-plugins.adoc#build-tool-plugins-maven-plugin, Maven>> |
<<build-tool-plugins.adoc#build-tool-plugins-gradle-plugin, Gradle>>
* *Customizing DSL:*
<<advanced.adoc#contract-dsl-customization, DSL customization>> |
<<advanced.adoc#contract-dsl-extending-common-jar, Common JAR>> |
<<advanced.adoc#contract-dsl-test-dep, Test dependency>> |
<<advanced.adoc#contract-dsl-plugin-dep, Plugin dependency>> |
<<advanced.adoc#contract-dsl-referencing, Referencing DSL>>
* *Customizing WireMock:*
<<advanced.adoc#customization-wiremock-extension, Extensions>> |
<<advanced.adoc#customization-wiremock-configuration, Configuration>>
* *Customizing {project-full-name}:*
<<advanced.adoc#contract-dsl-pluggable-architecture, Pluggable architecture>> |
<<advanced.adoc#contract-dsl-custom-contract-converter, Contract Converter>> |
<<advanced.adoc#contract-dsl-custom-test-generator, Test Generator>> |
<<advanced.adoc#contract-dsl-custom-stub-generator, Stub Generator>> |
<<advanced.adoc#contract-dsl-custom-stub-runner, Stub Runner>> |
<<advanced.adoc#contract-dsl-custom-stub-downloader, Stub Downloader>>
* *Appendix:*
<<appendix.adoc#common-application-properties, Application Properties>> |
<<appendix.adoc#auto-configuration-classes, Auto-configuration classes>> |

File diff suppressed because it is too large Load Diff

View File

@@ -714,25 +714,247 @@ to store and share Pact definitions. Starting from Spring Cloud Contract
2.0.0 one can fetch Pact files from the Pact Broker to generate
tests and stubs.
As a prerequisite the Pact Converter and Pact Stub Downloader
are required. You have to add them via the `spring-cloud-contract-pact` dependency.
You can read more about it in the <<pact-converter>> section.
IMPORTANT: Pact follows the Consumer Contract convention. That means
that the Consumer creates the Pact definitions first, then
shares the files with the Producer. Those expectations are generated
from the Consumer's code and can break the Producer if the expectations
are not met.
[[how-to-use-pact-broker-pact]]
=== How to work with Pact
Spring Cloud Contract includes support for https://docs.pact.io/[Pact] representation of
contracts up until v4. Instead of using the 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.
[[how-to-use-pact-broker-pact-converter]]
=== Pact Converter
In order to properly support the Spring Cloud Contract way of doing messaging
with Pact you'll have to provide some additional meta data entries. Below you can find a list of such entries:
* to define the destination to which a message gets sent, you have to
set
** `metaData` entry in the Pact file
*** with key `sentTo` equal to the destination to which a message is to be sent.
*** E.g. `"metaData": { "sentTo": "activemq:output" }`
[[how-to-use-pact-broker-pact-contract]]
=== Pact Contract
Spring Cloud Contract can read the Pact JSON definition out of the box. It's enough to place the file under `src/test/resources/contracts` folder. An example of such a Pact contract can look like this:
[source,javascript,indent=0]
----
include::{standalone_pact_path}/pact-http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.json[indent=0]
----
[[how-to-use-pact-broker-pact-for-producers]]
=== 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 and stub will be generated.
====
[source,java,indent=0,subs="verbatim,attributes",role="primary"]
.test
----
@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");
}
----
[source,json,indent=0,subs="verbatim,attributes",role="secondary"]
.stub
----
{
"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" ]
},
}
----
====
[[how-to-use-pact-broker-pact-consumers]]
=== 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]
----
====
[[pact-stub-downloader]]
=== Communicating with the Pact Broker
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
.Pact 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
|====
[[how-to-pact-consumer]]
=== Pact Consumer
=== Flow: Consumer Contract approach with Pact Broker | Consumer Side
The consumer uses Pact framework to generate Pact files. The
Pact files are sent to the Pact Broker. An example of such
setup can be found https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/{samples_branch}/consumer_pact[here].
[[how-to-pact-producer]]
=== Producer
=== Flow: Consumer Contract approach with Pact Broker | Producer Side
For the producer, to use the Pact files from the Pact Broker, we can reuse the
same mechanism we use for external contracts. We route Spring Cloud Contract
@@ -816,7 +1038,7 @@ With such a setup:
* The JAR with the stubs gets automatically created as usual
[[how-to-pact-consumer-producer-contract]]
=== Pact Consumer (Producer Contract approach)
=== Flow: Producer Contract approach with Pact | Consumer Side
In the scenario where you don't want to do Consumer Contract approach
(for every single consumer define the expectations) but you'd prefer
@@ -900,9 +1122,6 @@ With such a setup:
* Spring Cloud Contract will convert the Pact files into stub definitions
* The stub servers will be started and fed with stubs
For more information about Pact support you can go to
the <<pact-stub-downloader>> section.
[[how-to-debug]]
== How can I debug the request/response being sent by the generated tests client?
@@ -1048,4 +1267,43 @@ For example, you might decide to have no dependencies at all.
[[how-to-work-with-transitivie-exclude]]
=== Exclude dependencies on the consumer side
As a consumer, if you add the stub dependency to your classpath, you can explicitly exclude the unwanted dependencies.
As a consumer, if you add the stub dependency to your classpath, you can explicitly exclude the unwanted dependencies.
[[contract-dsl-rest-docs]]
== Generating Spring REST Docs snippets from the contracts
When you want to include the requests and responses of your API using Spring REST Docs,
you only need to make some minor changes to your setup if you are using MockMvc and RestAssuredMockMvc.
Simply include the following dependencies if you haven't already.
====
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.maven
----
include::{standalone_restdocs_path}/http-server/pom.xml[tags=dependencies,indent=0]
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.gradle
----
include::{standalone_restdocs_path}/http-server/build.gradle[tags=dependencies,indent=0]
----
====
Next you need to make some changes to your base class. Below you can find examples for using `WebAppContext` and the standalone option when using RestAssured.
====
[source,java,indent=0,subs="verbatim,attributes",role="primary"]
.WebAppContext
----
include::{standalone_restdocs_path}/http-server/src/test/java/com/example/fraud/FraudBaseWithWebAppSetup.java[tags=base_class,indent=0]
----
[source,java,indent=0,subs="verbatim,attributes",role="secondary"]
.Standalone
----
include::{standalone_restdocs_path}/http-server/src/test/java/com/example/fraud/FraudBaseWithStandaloneSetup.java[tags=base_class,indent=0]
----
====
TIP: You don't need to specify the output directory for the generated snippets since version 1.2.0.RELEASE of Spring REST Docs.

View File

@@ -11,8 +11,6 @@ The reference documentation consists of the following sections:
<<documentation-overview.adoc#contract-documentation,Documentation Overview>> :: About the Documentation, Getting Help, First Steps, and more.
<<getting-started.adoc#getting-started,Getting Started>> :: Introducing {project-full-name}, System Requirements, Developing Your First {project-full-name} based Application
<<using.adoc#using,Using {project-full-name}>> :: {project-full-name} usage examples and workflows
<<project-features.adoc#features,{project-full-name} Features>> :: Profiles, Logging, Security, Caching, Spring Integration, Testing, and more.
<<production-ready-features.adoc#production-ready,Spring Boot Actuator>> :: Monitoring, Metrics, Auditing, and more.
<<build-tool-plugins.adoc#build-tool-plugins,Build Tool Plugins>> :: Maven Plugin, Gradle Plugin.
<<howto.adoc#howto,"`How-to`" Guides>> :: Application Development, Configuration, Embedded Servers, Data Access, and many more.
<<appendix.adoc#appendix,Appendices>> :: Properties, Metadata, Configuration, Dependencies, and more.
<<project-features.adoc#features,{project-full-name} Features>> :: Profiles, Logging, Security, Caching, Spring Integration, Testing, and more.W
<<project-features.adoc#features-build-tools,Build Tools>> :: Maven Plugin, Gradle Plugin, Docker
<<howto.adoc#howto,"`How-to`" Guides>> :: Stubs versioning, Pact integration, Debugging, and more.

View File

@@ -9,6 +9,4 @@ include::documentation-overview.adoc[leveloffset=+1]
include::getting-started.adoc[leveloffset=+1]
include::using.adoc[leveloffset=+1]
include::project-features.adoc[leveloffset=+1]
include::build-tool-plugins.adoc[leveloffset=+1]
include::howto.adoc[leveloffset=+1]
include::appendix.adoc[leveloffset=+1]
include::howto.adoc[leveloffset=+1]

View File

@@ -9,6 +9,4 @@ include::documentation-overview.adoc[leveloffset=+1]
include::getting-started.adoc[leveloffset=+1]
include::using.adoc[leveloffset=+1]
include::project-features.adoc[leveloffset=+1]
include::build-tool-plugins.adoc[leveloffset=+1]
include::howto.adoc[leveloffset=+1]
include::appendix.adoc[leveloffset=+1]
include::howto.adoc[leveloffset=+1]

View File

@@ -8,6 +8,24 @@ might want to read the "<<getting-started.adoc#getting-started>>" and
"<<using.adoc#using>>" sections, so that you have a good grounding of the
basics.
include::_project-features-contract.adoc[]
include::_project-features-flows.adoc[]
include::_project-features-messaging.adoc[]
include::_project-features-stubrunner.adoc[]
include::_project-features-wiremock.adoc[]
[[features-build-tools]]
== Build Tools integration
You can run test generation / stub execution in various ways. The most common ones are
* link:maven-project.html[Maven]
* link:gradle-project.html[Gradle]
* link:docker-project.html[Docker]
[[features-whats-next]]
== What to Read Next
@@ -16,4 +34,5 @@ If you want to learn more about any of the classes discussed in this section, yo
<<howto.adoc#howto, how-to>> section.
If you are comfortable with {project-full-name}'s core features, you can continue on and read
about ...
about
_<<advanced.adoc, {project-full-name} Advanced features>>_.

View File

@@ -21,6 +21,7 @@ package org.springframework.cloud.contract.spec.internal;
*/
interface RegexCreatingProperty<T extends DslProperty> {
// tag::regex_creating_props[]
T anyAlphaUnicode();
T anyAlphaNumeric();
@@ -62,5 +63,6 @@ interface RegexCreatingProperty<T extends DslProperty> {
T anyNonEmptyString();
T anyOf(String... values);
// end::regex_creating_props[]
}

View File

@@ -62,7 +62,6 @@ public class StubRunnerRuleJUnitTest {
return "";
}
}
// end::test[]
// tag::test[]
@Test
@@ -90,6 +89,7 @@ public class StubRunnerRuleJUnitTest {
then(httpGet(rule.findStubUrl("fraudDetectionServer").toString() + "/name"))
.isEqualTo("fraudDetectionServer");
}
// end::test[]
private String httpGet(String url) throws Exception {
try (InputStream stream = URI.create(url).toURL().openStream()) {

View File

@@ -101,6 +101,22 @@ class ContractHttpDocsSpec extends Specification {
}
// end::http_dsl[]
org.springframework.cloud.contract.spec.Contract methodDsl =
org.springframework.cloud.contract.spec.Contract.make {
request {
// tag::method[]
method GET()
// end::method[]
url "/foo"
}
response {
status 200
}
priority 1
}
org.springframework.cloud.contract.spec.Contract request =
// tag::request[]
org.springframework.cloud.contract.spec.Contract.make {

View File

@@ -21,7 +21,7 @@ request:
queryParameters:
a: b
b: c
#tag::query_params[]
#end::query_params[]
#tag::headers[]
headers:
foo: bar

View File

@@ -1,125 +0,0 @@
=== Stub Runner Spring AMQP
Spring Cloud Contract Verifier Stub Runner's messaging module provides an easy way to
integrate with Spring AMQP's Rabbit Template. For the provided artifacts, it
automatically downloads the stubs and registers the required routes.
The integration tries to work standalone (that is, without interaction with a running
RabbitMQ message broker). It expects a `RabbitTemplate` on the application context and
uses it as a spring boot test named `@SpyBean`. As a result, it can use the mockito spy
functionality to verify and inspect messages sent by the application.
On the message consumer side, the stub runner considers all `@RabbitListener` annotated
endpoints and all `SimpleMessageListenerContainer` objects on the application context.
As messages are usually sent to exchanges in AMQP, the message contract contains the
exchange name as the destination. Message listeners on the other side are bound to
queues. Bindings connect an exchange to a queue. If message contracts are triggered, the
Spring AMQP stub runner integration looks for bindings on the application context that
match this exchange. Then it collects the queues from the Spring exchanges and tries to
find message listeners bound to these queues. The message is triggered for all matching
message listeners.
If you need to work with routing keys, it's enough to pass them via the `amqp_receivedRoutingKey`
messaging header.
==== Adding the Runner to the Project
You can have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and
set the property `stubrunner.amqp.enabled=true`. Remember to annotate your test class
with `@AutoConfigureStubRunner`.
IMPORTANT: If you already have Stream and Integration on the classpath, you need
to disable them explicitly by setting the `stubrunner.stream.enabled=false` and
`stubrunner.integration.enabled=false` properties.
Assume that you have the following Maven repository with a deployed stubs for the
`spring-cloud-contract-amqp-test` application.
[source,bash,indent=0]
----
└── .m2
└── repository
└── com
└── example
└── spring-cloud-contract-amqp-test
├── 0.4.0-SNAPSHOT
│   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT.pom
│   ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume that the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── contracts
└── shouldProduceValidPersonData.groovy
----
Consider the following contract:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=amqp_contract,indent=0]
----
Now consider the following Spring configuration:
[source,yaml]
----
include::src/test/resources/application.yml[]
----
===== Triggering the message
To trigger a message using the contract above, use the `StubTrigger` interface as
follows:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
The message has a destination of `contract-test.exchange`, so the Spring AMQP stub runner
integration looks for bindings related to this exchange.
[source,java]
----
include::src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_binding,indent=0]
----
The binding definition binds the queue `test.queue`. As a result, the following listener
definition is matched and invoked with the contract message.
[source,java]
----
include::src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/AmqpMessagingApplication.java[tags=amqp_listener,indent=0]
----
Also, the following annotated listener matches and is invoked:
[source,java]
----
include::src/main/java/org/springframework/cloud/contract/stubrunner/messaging/amqp/MessageSubscriberRabbitListener.java[tags=amqp_annotated_listener,indent=0]
----
NOTE: The message is directly handed over to the `onMessage` method of the
`MessageListener` associated with the matching `SimpleMessageListenerContainer`.
===== Spring AMQP Test Configuration
In order to avoid Spring AMQP trying to connect to a running broker during our tests
configure a mock `ConnectionFactory`.
To disable the mocked ConnectionFactory, set the following property:
`stubrunner.amqp.mockConnection=false`
[source,yaml]
----
stubrunner:
amqp:
mockConnection: false
----

View File

@@ -1,123 +0,0 @@
:input_name: jms:input
:output_name: jms:output
=== Stub Runner Camel
Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to integrate with Apache Camel.
For the provided artifacts it will automatically download the stubs and register the required
routes.
==== Adding it to the project
It's enough to have both Apache Camel and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with `@AutoConfigureStubRunner`.
==== Disabling the functionality
If you need to disable this functionality just pass `stubrunner.camel.enabled=false` property.
==== Examples
===== Stubs structure
Let us assume that we have the following Maven repository with a deployed stubs for the
`camelService` application.
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── camelService
├── 0.0.1-SNAPSHOT
│   ├── camelService-0.0.1-SNAPSHOT.pom
│   ├── camelService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
And the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Let's consider the following contracts (let' number it with *1*):
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
and number *2*
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
===== Scenario 1 (no input message)
So as to trigger a message via the `return_book_1` label we'll use the `StubTigger` interface as follows
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
Next we'll want to listen to the output of the message sent to `{output_name}`
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
And the received message would pass the following assertions
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
===== Scenario 2 (output triggered by input)
Since the route is set for you it's enough to just send a message to the `{output_name}` destination.
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_send,indent=0]
----
Next we'll want to listen to the output of the message sent to `{output_name}`
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
And the received message would pass the following assertions
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
===== Scenario 3 (input with no output)
Since the route is set for you it's enough to just send a message to the `{output_name}` destination.
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/camel/CamelStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----

View File

@@ -1,138 +0,0 @@
:input_name: input
:output_name: output
=== Stub Runner Integration
Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to
integrate with Spring Integration. For the provided artifacts, it automatically downloads
the stubs and registers the required routes.
==== Adding the Runner to the Project
You can have both Spring Integration and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with `@AutoConfigureStubRunner`.
==== Disabling the functionality
If you need to disable this functionality, set the
`stubrunner.integration.enabled=false` property.
Assume that you have the following Maven repository with deployed stubs for the
`integrationService` application:
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── integrationService
├── 0.0.1-SNAPSHOT
│   ├── integrationService-0.0.1-SNAPSHOT.pom
│   ├── integrationService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Consider the following contracts (numbered *1*):
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
Now consider *2*:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
and the following Spring Integration Route:
[source,xml]
----
include::src/test/resources/integration-context.xml[lines=1;18..-1]
----
These examples lend themselves to three scenarios:
* <<integration-scenario-1>>
* <<integration-scenario-2>>
* <<integration-scenario-3>>
[[integration-scenario-1]]
===== Scenario 1 (no input message)
To trigger a message via the `return_book_1` label, use the `StubTigger` interface, as
follows:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
To listen to the output of the message sent to `{output_name}`:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
The received message would pass the following assertions:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
[[integration-scenario-2]]
===== Scenario 2 (output triggered by input)
Since the route is set for you, you can send a message to the `{output_name}`
destination:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_send,indent=0]
----
To listen to the output of the message sent to `{output_name}`:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
[[integration-scenario-3]]
===== Scenario 3 (input with no output)
Since the route is set for you, you can send a message to the `{input_name}` destination:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/integration/IntegrationStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----

View File

@@ -1,160 +0,0 @@
=== Stub Runner Stream
Spring Cloud Contract Verifier Stub Runner's messaging module gives you an easy way to
integrate with Spring Stream. For the provided artifacts, it automatically downloads the
stubs and registers the required routes.
WARNING: If Stub Runner's integration with Stream the `messageFrom` or `sentTo` Strings
are resolved first as a `destination` of a channel and no such `destination` exists, the
destination is resolved as a channel name.
IMPORTANT: If you want to use Spring Cloud Stream remember, to add a dependency on
`org.springframework.cloud:spring-cloud-stream-test-support`.
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
.Maven
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
----
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
.Gradle
----
testCompile "org.springframework.cloud:spring-cloud-stream-test-support"
----
==== Adding the Runner to the Project
You can have both Spring Cloud Stream and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with `@AutoConfigureStubRunner`.
==== Disabling the functionality
If you need to disable this functionality, set the `stubrunner.stream.enabled=false`
property.
Assume that you have the following Maven repository with a deployed stubs for the
`streamService` application:
[source,bash,indent=0]
----
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── streamService
├── 0.0.1-SNAPSHOT
│   ├── streamService-0.0.1-SNAPSHOT.pom
│   ├── streamService-0.0.1-SNAPSHOT-stubs.jar
│   └── maven-metadata-local.xml
└── maven-metadata-local.xml
----
Further assume the stubs contain the following structure:
[source,bash,indent=0]
----
├── META-INF
│   └── MANIFEST.MF
└── repository
├── accurest
│   ├── bookDeleted.groovy
│   ├── bookReturned1.groovy
│   └── bookReturned2.groovy
└── mappings
----
Consider the following contracts (numbered *1*):
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl,indent=0]
----
Now consider *2*:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=sample_dsl_2,indent=0]
----
Now consider the following Spring configuration:
[source,yaml]
----
include::src/test/resources/application.yml[]
----
These examples lend themselves to three scenarios:
* <<stream-scenario-1>>
* <<stream-scenario-2>>
* <<stream-scenario-3>>
[[stream-scenario-1]]
===== Scenario 1 (no input message)
To trigger a message via the `return_book_1` label, use the `StubTrigger` interface as
follows:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger,indent=0]
----
To listen to the output of the message sent to a channel whose `destination` is
`returnBook`:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_trigger_message,indent=0]
----
[[stream-scenario-2]]
===== Scenario 2 (output triggered by input)
Since the route is set for you, you can send a message to the `bookStorage`
`destination`:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_send,indent=0]
----
To listen to the output of the message sent to `returnBook`:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive,indent=0]
----
The received message passes the following assertions:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=client_receive_message,indent=0]
----
[[stream-scenario-3]]
===== Scenario 3 (input with no output)
Since the route is set for you, you can send a message to the `{output_name}`
destination:
[source,groovy]
----
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_no_output,indent=0]
----