Working docs
This commit is contained in:
1539
README.adoc
1539
README.adoc
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
@@ -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].
|
||||
@@ -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]
|
||||
File diff suppressed because it is too large
Load Diff
417
docs/src/main/asciidoc/_project-features-flows.adoc
Normal file
417
docs/src/main/asciidoc/_project-features-flows.adoc
Normal 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`.
|
||||
922
docs/src/main/asciidoc/_project-features-messaging.adoc
Normal file
922
docs/src/main/asciidoc/_project-features-messaging.adoc
Normal 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
|
||||
----
|
||||
@@ -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].
|
||||
175
docs/src/main/asciidoc/_project-features-wiremock.adoc
Normal file
175
docs/src/main/asciidoc/_project-features-wiremock.adoc
Normal 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).
|
||||
@@ -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[]
|
||||
@@ -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`.
|
||||
@@ -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].
|
||||
@@ -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]
|
||||
----
|
||||
@@ -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]
|
||||
@@ -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"`
|
||||
@@ -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[]
|
||||
373
docs/src/main/asciidoc/advanced.adoc
Normal file
373
docs/src/main/asciidoc/advanced.adoc
Normal 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`
|
||||
|====
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
@@ -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]
|
||||
@@ -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>>_.
|
||||
|
||||
@@ -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[]
|
||||
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -21,7 +21,7 @@ request:
|
||||
queryParameters:
|
||||
a: b
|
||||
b: c
|
||||
#tag::query_params[]
|
||||
#end::query_params[]
|
||||
#tag::headers[]
|
||||
headers:
|
||||
foo: bar
|
||||
|
||||
@@ -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
|
||||
----
|
||||
@@ -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]
|
||||
----
|
||||
@@ -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]
|
||||
----
|
||||
@@ -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]
|
||||
----
|
||||
Reference in New Issue
Block a user