Files
spring-cloud-contract/docs/modules/ROOT/pages/project-features-stubrunner/stub-runner-core.adoc
Marcin Grzejszczak 43318b0b05 Done
2023-09-11 19:00:22 +02:00

307 lines
9.7 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[[features-stub-runner-core]]
= Stub Runner Core
include::partial$_attributes.adoc[]
The stub runner core runs stubs for service collaborators. Treating stubs as contracts of
services lets you use stub-runner as an implementation of
https://martinfowler.com/articles/consumerDrivenContracts.html[Consumer-driven Contracts].
Stub Runner lets you 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 from the following options of acquiring stubs:
- Aether-based solution that downloads JARs with stubs from Artifactory or Nexus
- Classpath-scanning solution that searches the classpath with a pattern to retrieve stubs
- Writing your own implementation of the `org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder` for full customization
The latter example is described in the xref:advanced.adoc#customization-custom-stub-runner[Custom Stub Runner] section.
[[features-stub-runner-downloading-stub]]
=== Downloading Stubs
You can control the downloading of stubs with the `stubsMode` switch. It picks value from the
`StubRunnerProperties.StubsMode` enumeration. You can use the following options:
- `StubRunnerProperties.StubsMode.CLASSPATH` (default value): Picks stubs from the classpath
- `StubRunnerProperties.StubsMode.LOCAL`: Picks stubs from a local storage (for example, `.m2`)
- `StubRunnerProperties.StubsMode.REMOTE`: Picks stubs from a remote location
The following example picks stubs from a local location:
====
[source,java]
----
@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`
(or set nothing since `CLASSPATH` is the default value), the classpath is scanned.
Consider the following example:
====
[source,java]
----
@AutoConfigureStubRunner(ids = {
"com.example:beer-api-producer:+:stubs:8095",
"com.example.foo:bar:1.0.0:superstubs:8096"
})
----
====
You can add the dependencies to your classpath, as follows:
====
[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.thing1</groupId>
<artifactId>thing2</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.thing1:thing2:1.0.0:superstubs") {
transitive = false
}
----
====
Then the specified locations on your classpath get scanned. For `com.example:beer-api-producer-restdocs`,
the following locations are scanned:
- /META-INF/com.example/beer-api-producer-restdocs/**/*.*
- /contracts/com.example/beer-api-producer-restdocs/**/*.*
- /mappings/com.example/beer-api-producer-restdocs/**/*.*
For `com.example.thing1:thing2`, the following locations are scanned:
- /META-INF/com.example.thing1/thing2/**/*.*
- /contracts/com.example.thing1/thing2/**/*.*
- /mappings/com.example.thing1/thing2/**/*.*
TIP: You have to explicitly provide the group and artifact IDs when you package the
producer stubs.
To achieve proper stub packaging, the producer would set up the contracts as follows:
====
[source,bash]
----
└── src
└── test
└── resources
└── contracts
   └── com.example
      └── beer-api-producer-restdocs
      └── nested
      └── contract3.groovy
----
====
By using the https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/{samples_branch}/producer_with_restdocs/pom.xml[Maven `assembly` plugin] or the
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, the classpath gets scanned and you can profit from the messaging or
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 underlying
concrete implementation of the HTTP server (for example, WireMock is one of the implementations).
Sometimes, you need to perform some additional tuning (which is concrete for the given implementation) of the stub servers.
To do that, Stub Runner gives you
the `httpServerStubConfigurer` property that is available in the annotation and the
JUnit rule and is accessible through system properties, where you can provide
your implementation of the `org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer`
interface. The implementations can alter
the configuration files for the given HTTP server stub.
Spring Cloud Contract Stub Runner comes with an implementation that you
can extend for WireMock:
`org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer`.
In the `configure` method,
you can provide your own custom configuration for the given stub. The use
case might be starting WireMock for the given artifact ID, on an HTTPS port. The following
example shows how to do so:
.WireMockHttpServerStubConfigurer implementation
====
[source,groovy,indent=0]
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=wireMockHttpServerStubConfigurer]
----
====
You can then reuse it with the `@AutoConfigureStubRunner` annotation, as follows:
====
[source,groovy,indent=0]
----
include::{stubrunner_core_path}/src/test/groovy/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfigurationSpec.groovy[tags=annotation]
----
====
Whenever an HTTPS port is found, it takes precedence over the HTTP port.
[[features-stub-runner-running-stubs]]
== Running stubs
This section describes how to run stubs. It contains the following topics:
* xref:../project-features-stubrunner/stub-runner-core.adoc#features-stub-runner-http-stubs[HTTP Stubs]
* xref:../project-features-stubrunner/stub-runner-core.adoc#features-stub-runner-viewing[Viewing Registered Mappings]
* xref:../project-features-stubrunner/stub-runner-core.adoc#features-stub-runner-messaging[Messaging Stubs]
[[features-stub-runner-http-stubs]]
=== HTTP Stubs
Stubs are defined in JSON documents, whose syntax is defined in the http://wiremock.org/stubbing.html[WireMock documentation].
The following example defines a stub in JSON:
====
[source,javascript,indent=0]
----
{
"request": {
"method": "GET",
"url": "/ping"
},
"response": {
"status": 200,
"body": "pong",
"headers": {
"Content-Type": "text/plain"
}
}
}
----
====
[[features-stub-runner-viewing]]
=== Viewing Registered Mappings
Every stubbed collaborator exposes a list of defined mappings under the `__/admin/` endpoint.
You can also use the `mappingsOutputFolder` property to dump the mappings to files.
For the annotation-based approach, it would resembling the following example:
====
[source,java]
----
@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
mappingsOutputFolder = "target/outputmappings/")
----
====
For the JUnit approach, it resembles the following example:
====
[source,java]
----
@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
.repoRoot("https://some_url")
.downloadStub("a.b.c", "loanIssuance")
.downloadStub("a.b.c:fraudDetectionServer")
.withMappingsOutputFolder("target/outputmappings")
----
====
Then, if you check out the `target/outputmappings` folder, 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)
the 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"
},
...
]
----
====
[[features-stub-runner-messaging]]
=== Messaging Stubs
Depending on the provided Stub Runner dependency and the DSL, the messaging routes are automatically set up.