682 lines
24 KiB
Plaintext
682 lines
24 KiB
Plaintext
:branch: master
|
||
:samples_branch: 2.0.x
|
||
|
||
=== Stub Runner Core
|
||
|
||
Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
|
||
http://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.
|
||
|
||
==== Retrieving stubs
|
||
|
||
You can pick the following options of acquiring stubs
|
||
|
||
- Aether based solution that downloads JARs with stubs from Artifactory / Nexus
|
||
- 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
|
||
|
||
The latter example is described in the <<custom_stub_runner, Custom Stub Runner>> section.
|
||
|
||
===== Stub downloading
|
||
|
||
You can control the stub downloading via the `stubsMode` switch. It picks value from the
|
||
`StubRunnerProperties.StubsMode` enum. You can use the following options
|
||
|
||
- `StubRunnerProperties.StubsMode.CLASSPATH` (default value) - will pick stubs from the classpath
|
||
- `StubRunnerProperties.StubsMode.LOCAL` - will pick stubs from a local storage (e.g. `.m2`)
|
||
- `StubRunnerProperties.StubsMode.REMOTE` - will pick stubs from a remote location
|
||
|
||
Example:
|
||
|
||
[source,java]
|
||
----
|
||
@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)
|
||
----
|
||
|
||
===== Classpath scanning
|
||
|
||
If you set the `stubsMode` property to `StubRunnerProperties.StubsMode.CLASSPATH`
|
||
(or set nothing since `CLASSPATH` is the default value) then classpath will get scanned.
|
||
Let's look at the following example:
|
||
|
||
[source,java]
|
||
----
|
||
@AutoConfigureStubRunner(ids = {
|
||
"com.example:beer-api-producer:+:stubs:8095",
|
||
"com.example.foo:bar:1.0.0:superstubs:8096"
|
||
})
|
||
----
|
||
|
||
If you've added the dependencies to your classpath
|
||
|
||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||
.Maven
|
||
----
|
||
<dependency>
|
||
<groupId>com.example</groupId>
|
||
<artifactId>beer-api-producer-restdocs</artifactId>
|
||
<classifier>stubs</classifier>
|
||
<version>0.0.1-SNAPSHOT</version>
|
||
<scope>test</scope>
|
||
<exclusions>
|
||
<exclusion>
|
||
<groupId>*</groupId>
|
||
<artifactId>*</artifactId>
|
||
</exclusion>
|
||
</exclusions>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>com.example.foo</groupId>
|
||
<artifactId>bar</artifactId>
|
||
<classifier>superstubs</classifier>
|
||
<version>1.0.0</version>
|
||
<scope>test</scope>
|
||
<exclusions>
|
||
<exclusion>
|
||
<groupId>*</groupId>
|
||
<artifactId>*</artifactId>
|
||
</exclusion>
|
||
</exclusions>
|
||
</dependency>
|
||
----
|
||
|
||
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
|
||
.Gradle
|
||
----
|
||
testCompile("com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs") {
|
||
transitive = false
|
||
}
|
||
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`
|
||
|
||
- /META-INF/com.example/beer-api-producer-restdocs/**/*.*
|
||
- /contracts/com.example/beer-api-producer-restdocs/**/*.*
|
||
- /mappings/com.example/beer-api-producer-restdocs/**/*.*
|
||
|
||
and `com.example.foo:bar`
|
||
|
||
- /META-INF/com.example.foo/bar/**/*.*
|
||
- /contracts/com.example.foo/bar/**/*.*
|
||
- /mappings/com.example.foo/bar/**/*.*
|
||
|
||
TIP: As you can see you have to explicitly provide the group and artifact ids when packaging the
|
||
producer stubs.
|
||
|
||
The producer would setup the contracts like this:
|
||
|
||
[source,bash]
|
||
----
|
||
└── src
|
||
└── test
|
||
└── resources
|
||
└── contracts
|
||
└── com.example
|
||
└── beer-api-producer-restdocs
|
||
└── nested
|
||
└── contract3.groovy
|
||
|
||
----
|
||
|
||
To achieve proper stub packaging.
|
||
|
||
Or using the https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/{samples_branch}/producer_with_restdocs/pom.xml[Maven `assembly` plugin] or
|
||
https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/{samples_branch}/producer_with_restdocs/build.gradle[Gradle Jar] task you have to create the following
|
||
structure in your stubs jar.
|
||
|
||
[source,bash]
|
||
----
|
||
└── META-INF
|
||
└── com.example
|
||
└── beer-api-producer-restdocs
|
||
└── 2.0.0
|
||
├── contracts
|
||
│ └── nested
|
||
│ └── contract2.groovy
|
||
└── mappings
|
||
└── mapping.json
|
||
|
||
----
|
||
|
||
By maintaining this structure classpath gets scanned and you can profit from the messaging /
|
||
HTTP stubs without the need to download artifacts.
|
||
|
||
==== Running stubs
|
||
|
||
===== Limitations
|
||
|
||
IMPORTANT: There might be a problem with StubRunner shutting down ports between tests. You might
|
||
have a situation in which you get port conflicts. As long as you use the same context across tests
|
||
everything works fine. But when the context are different (e.g. different stubs or different profiles)
|
||
then you have to either use `@DirtiesContext` to shut down the stub servers, or else run them on
|
||
different ports per test.
|
||
|
||
===== 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
|
||
----
|
||
|
||
===== HTTP Stubs
|
||
|
||
Stubs are defined in JSON documents, whose syntax is defined in http://wiremock.org/stubbing.html[WireMock documentation]
|
||
|
||
Example:
|
||
|
||
[source,javascript,indent=0]
|
||
----
|
||
{
|
||
"request": {
|
||
"method": "GET",
|
||
"url": "/ping"
|
||
},
|
||
"response": {
|
||
"status": 200,
|
||
"body": "pong",
|
||
"headers": {
|
||
"Content-Type": "text/plain"
|
||
}
|
||
}
|
||
}
|
||
----
|
||
|
||
===== Viewing registered mappings
|
||
|
||
Every stubbed collaborator exposes list of defined mappings under `__/admin/` endpoint.
|
||
|
||
You can also use the `mappingsOutputFolder` property to dump the mappings to files.
|
||
For annotation based approach it would look like this
|
||
|
||
[source,java]
|
||
----
|
||
@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
|
||
mappingsOutputFolder = "target/outputmappings/")
|
||
----
|
||
|
||
and for the JUnit approach like this:
|
||
|
||
[source,java]
|
||
----
|
||
@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
|
||
.repoRoot("http://some_url")
|
||
.downloadStub("a.b.c", "loanIssuance")
|
||
.downloadStub("a.b.c:fraudDetectionServer")
|
||
.withMappingsOutputFolder("target/outputmappings")
|
||
----
|
||
|
||
Then if you check out the folder `target/outputmappings` you would see the following structure
|
||
|
||
[source,bash]
|
||
----
|
||
.
|
||
├── fraudDetectionServer_13705
|
||
└── loanIssuance_12255
|
||
----
|
||
|
||
That means that there were two stubs registered. `fraudDetectionServer` was registered at port `13705`
|
||
and `loanIssuance` at port `12255`. If we take a look at one of the files we would see (for WireMock)
|
||
mappings available for the given server:
|
||
|
||
[source,json]
|
||
----
|
||
[{
|
||
"id" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7",
|
||
"request" : {
|
||
"url" : "/name",
|
||
"method" : "GET"
|
||
},
|
||
"response" : {
|
||
"status" : 200,
|
||
"body" : "fraudDetectionServer"
|
||
},
|
||
"uuid" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7"
|
||
},
|
||
...
|
||
]
|
||
----
|
||
|
||
===== Messaging Stubs
|
||
|
||
Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.
|
||
|
||
=== Stub Runner JUnit Rule
|
||
|
||
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]
|
||
----
|
||
|
||
After that rule gets executed Stub Runner connects to your Maven repository and for the given list of dependencies tries to:
|
||
|
||
- download them
|
||
- cache them locally
|
||
- unzip them to a temporary folder
|
||
- start a WireMock server for each Maven dependency on a random port from the provided range of ports / provided port
|
||
- 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.
|
||
Check their https://wiki.eclipse.org/Aether[docs] for more information.
|
||
|
||
Since the `StubRunnerRule` implements the `StubFinder` it allows you to find the started stubs:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::src/main/java/org/springframework/cloud/contract/stubrunner/StubFinder.java[lines=16..-1]
|
||
----
|
||
|
||
Example of usage in Spock tests:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleSpec.groovy[tags=classrule]
|
||
----
|
||
|
||
Example of usage in JUnit tests:
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRuleJUnitTest.java[tags=test]
|
||
----
|
||
|
||
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 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.
|
||
|
||
==== 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.
|
||
|
||
==== 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.
|
||
|
||
==== Fluent API
|
||
|
||
When using the `StubRunnerRule` 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]
|
||
----
|
||
|
||
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]
|
||
----
|
||
|
||
==== 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
|
||
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
|
||
its methods as presented below:
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::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]
|
||
----
|
||
|
||
Instead of using the properties you can also use the properties inside the `@AutoConfigureStubRunner`.
|
||
Below you can find an example of achieving the same result by setting values on the annotation.
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerSpringCloudAutoConfigurationSpec.groovy[tags=autoconfigure]
|
||
----
|
||
|
||
Stub Runner Spring registers environment variables in the following manner
|
||
for every registered WireMock server. Example for Stub Runner ids
|
||
`com.example:foo`, `com.example:bar`.
|
||
|
||
- `stubrunner.runningstubs.foo.port`
|
||
- `stubrunner.runningstubs.com.example.foo.port`
|
||
- `stubrunner.runningstubs.bar.port`
|
||
- `stubrunner.runningstubs.com.example.bar.port`
|
||
|
||
Which you can reference in your code.
|
||
|
||
You can also use the `@StubRunnerPort` annotation to inject the port of a running stub.
|
||
Value of the annotation can be the `groupid:artifactid` or just the `artifactid`. Example for Stub Runner ids
|
||
`com.example:foo`, `com.example:bar`.
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
@StubRunnerPort("foo")
|
||
int fooPort;
|
||
@StubRunnerPort("com.example:bar")
|
||
int barPort;
|
||
----
|
||
|
||
=== Stub Runner Spring Cloud
|
||
|
||
Stub Runner can integrate with Spring Cloud.
|
||
|
||
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]
|
||
|
||
==== Stubbing Service Discovery
|
||
|
||
The most important feature of `Stub Runner Spring Cloud` is the fact that it's stubbing
|
||
|
||
- `DiscoveryClient`
|
||
- `Ribbon` `ServerList`
|
||
|
||
that means that regardless of the fact whether you're using Zookeeper, Consul, Eureka or anything else, you don't need that in your tests.
|
||
We're starting WireMock instances of your dependencies and we're telling your application whenever you're using `Feign`, load balanced `RestTemplate`
|
||
or `DiscoveryClient` directly, to call those stubbed servers instead of calling the real Service Discovery tool.
|
||
|
||
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]
|
||
----
|
||
|
||
for the following configuration file
|
||
|
||
[source,yml,indent=0]
|
||
----
|
||
include::src/test/resources/application.yml[tags=ids]
|
||
----
|
||
|
||
===== Test profiles and service discovery
|
||
|
||
In your integration tests you typically don't want to call neither a discovery service (e.g. Eureka)
|
||
or Config Server. That's why you create an additional test configuration in which you want to disable
|
||
these features.
|
||
|
||
Due to certain limitations of https://github.com/spring-cloud/spring-cloud-commons/issues/156[`spring-cloud-commons`] to achieve this you have disable these properties
|
||
via a static block like presented below (example for Eureka)
|
||
|
||
[source,java]
|
||
----
|
||
//Hack to work around https://github.com/spring-cloud/spring-cloud-commons/issues/156
|
||
static {
|
||
System.setProperty("eureka.client.enabled", "false");
|
||
System.setProperty("spring.cloud.config.failFast", "false");
|
||
}
|
||
----
|
||
|
||
==== Additional Configuration
|
||
|
||
You can match the artifactId of the stub with the name of your app by using the `stubrunner.idsToServiceIds:` map.
|
||
You can disable Stub Runner Ribbon support by providing: `stubrunner.cloud.ribbon.enabled` equal to `false`
|
||
You can disable Stub Runner support by providing: `stubrunner.cloud.enabled` equal to `false`
|
||
|
||
TIP: By default all service discovery will be stubbed. That means that regardless of the fact if you have
|
||
an existing `DiscoveryClient` its results will be ignored. However, if you want to reuse it, just set
|
||
`stubrunner.cloud.delegate.enabled` to `true` and then your existing `DiscoveryClient` results will be
|
||
merged with the stubbed ones.
|
||
|
||
The default Maven configuration used by Stub Runner can be tweaked either
|
||
via the following system properties or environment variables
|
||
|
||
- `maven.repo.local` - path to the custom maven local repository location
|
||
- `org.apache.maven.user-settings` - path to custom maven user settings location
|
||
- `org.apache.maven.global-settings` - path to maven global settings location
|
||
|
||
=== Stub Runner Boot Application
|
||
|
||
Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to
|
||
trigger the messaging labels and to access started WireMock servers.
|
||
|
||
One of the use-cases is to run some smoke (end to end) tests on a deployed application.
|
||
You can check out the https://github.com/spring-cloud/spring-cloud-pipelines[Spring Cloud Pipelines]
|
||
project for more information.
|
||
|
||
==== How to use it?
|
||
|
||
===== Stub Runner Server
|
||
|
||
Just add the
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
compile "org.springframework.cloud:spring-cloud-starter-stub-runner"
|
||
----
|
||
|
||
Annotate a class with `@EnableStubRunnerServer`, build a fat-jar and you're ready to go!
|
||
|
||
For the properties check the *Stub Runner Spring* section.
|
||
|
||
===== Stub Runner Server Fat Jar
|
||
|
||
You can download a standalone JAR from Maven (for example, for version 1.2.3.RELEASE), as follows:
|
||
|
||
[source,bash,indent=0]
|
||
----
|
||
$ wget -O stub-runner.jar 'https://search.maven.org/remote_content?g=org.springframework.cloud&a=spring-cloud-contract-stub-runner-boot&v=1.2.3.RELEASE'
|
||
$ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...
|
||
----
|
||
|
||
===== Spring Cloud CLI
|
||
|
||
Starting from `1.4.0.RELEASE` version of the http://cloud.spring.io/spring-cloud-cli[Spring Cloud CLI]
|
||
project you can start Stub Runner Boot by executing `spring cloud stubrunner`.
|
||
|
||
In order to pass the configuration just create a `stubrunner.yml` file in the current working directory
|
||
or a subdirectory called `config` or in `~/.spring-cloud`. The file could look like this
|
||
(example for running stubs installed locally)
|
||
|
||
.stubrunner.yml
|
||
[source,yml,indent=0]
|
||
----
|
||
stubrunner:
|
||
stubsMode: LOCAL
|
||
ids:
|
||
- com.example:beer-api-producer:+:9876
|
||
----
|
||
|
||
and then just call `spring cloud stubrunner` from your terminal window to start
|
||
the Stub Runner server. It will be available at port `8750`.
|
||
|
||
==== Endpoints
|
||
|
||
===== 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)
|
||
|
||
===== Messaging
|
||
|
||
For Messaging
|
||
|
||
- GET `/triggers` - returns a list of all running labels in `ivy : [ label1, label2 ...]` notation
|
||
- 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)
|
||
|
||
==== Example
|
||
|
||
[source,groovy,indent=0]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/server/StubRunnerBootSpec.groovy[tags=boot_usage]
|
||
----
|
||
|
||
==== 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?
|
||
Let's assume that you don't want to deploy 50 microservice to a test environment in order
|
||
to check if your application is working fine. You've already executed a suite of tests during the build process
|
||
but you would also like to ensure that the packaging of your application is fine. What you can do
|
||
is to deploy your application to an environment, start it and run a couple of tests on it to see if
|
||
it's working fine. We can call those tests smoke-tests since their idea is to check only a handful
|
||
of testing scenarios.
|
||
|
||
The problem with this approach is such that if you're doing microservices most likely you're
|
||
using a service discovery tool. Stub Runner Boot allows you to solve this issue by starting the
|
||
required stubs and register them in a service discovery tool. Let's take a look at an example of
|
||
such a setup with Eureka. Let's assume that Eureka was already running.
|
||
|
||
[source,java,indent=0]
|
||
----
|
||
include::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`
|
||
and we want to have the stub runner feature turned on `@AutoConfigureStubRunner`.
|
||
|
||
Now let's assume that we want to start this application so that the stubs get automatically registered.
|
||
We can do it by running the app `java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar` where
|
||
`${SYSTEM_PROPS}` would contain the following list of properties
|
||
|
||
[source,bash,indent=0]
|
||
----
|
||
include::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
|
||
discovery. Most likely points 1-3 could be set by default in `application.yml` cause they are not
|
||
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
|
||
|
||
There are cases in which 2 consumers of the same endpoint want to have 2 different responses.
|
||
|
||
TIP: This approach also allows you to immediately know which consumer is using which part of your API.
|
||
You can remove part of a response that your API produces and you can see which of your autogenerated tests
|
||
fails. If none fails then you can safely delete that part of the response cause nobody is using it.
|
||
|
||
Let's look at the following example for contract defined for the producer called `producer`.
|
||
There are 2 consumers: `foo-consumer` and `bar-consumer`.
|
||
|
||
*Consumer `foo-service`*
|
||
|
||
[source,groovy]
|
||
----
|
||
request {
|
||
url '/foo'
|
||
method GET()
|
||
}
|
||
response {
|
||
status OK()
|
||
body(
|
||
foo: "foo"
|
||
}
|
||
}
|
||
----
|
||
|
||
*Consumer `bar-service`*
|
||
|
||
[source,groovy]
|
||
----
|
||
request {
|
||
url '/foo'
|
||
method GET()
|
||
}
|
||
response {
|
||
status OK()
|
||
body(
|
||
bar: "bar"
|
||
}
|
||
}
|
||
----
|
||
|
||
You can't produce for the same request 2 different responses. That's why you can properly package the
|
||
contracts and then profit from the `stubsPerConsumer` feature.
|
||
|
||
On the producer side the consumers can have a folder that contains contracts related only to them.
|
||
By setting the `stubrunner.stubs-per-consumer` flag to `true` we no longer register all stubs but only those that
|
||
correspond to the consumer application's name. In other words we'll scan the path of every stub and
|
||
if it contains the subfolder with name of the consumer in the path only then will it get registered.
|
||
|
||
On the `foo` producer side the contracts would look like this
|
||
|
||
[source,bash]
|
||
----
|
||
.
|
||
└── contracts
|
||
├── bar-consumer
|
||
│ ├── bookReturnedForBar.groovy
|
||
│ └── shouldCallBar.groovy
|
||
└── foo-consumer
|
||
├── bookReturnedForFoo.groovy
|
||
└── shouldCallFoo.groovy
|
||
----
|
||
|
||
Being the `bar-consumer` consumer you can either set the `spring.application.name` or the `stubrunner.consumer-name` to `bar-consumer`
|
||
Or set the test as follows:
|
||
|
||
[source,groovy]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerSpec.groovy[tags=test]
|
||
...
|
||
}
|
||
----
|
||
|
||
Then only the stubs registered under a path that contains the `bar-consumer` in its name (i.e. those from the
|
||
`src/test/resources/contracts/bar-consumer/some/contracts/...` folder) will be allowed to be referenced.
|
||
|
||
Or set the consumer name explicitly
|
||
|
||
[source,groovy]
|
||
----
|
||
include::src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/cloud/StubRunnerStubsPerConsumerWithConsumerNameSpec.groovy[tags=test]
|
||
...
|
||
}
|
||
----
|
||
|
||
Then only the stubs registered under a path that contains the `foo-consumer` in its name (i.e. those from the
|
||
`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.
|
||
|