464 lines
15 KiB
Plaintext
464 lines
15 KiB
Plaintext
[[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 the 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 chapter, 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.
|
|
The following listing shows the `PatternUtils` class:
|
|
|
|
====
|
|
[source,java]
|
|
----
|
|
include::{samples_url}/common/src/main/java/com/example/PatternUtils.java[]
|
|
----
|
|
====
|
|
|
|
`ConsumerUtils` contains functions used by the consumer.
|
|
The following listing shows the `ConsumerUtils` class:
|
|
|
|
====
|
|
[source,java]
|
|
----
|
|
include::{samples_url}/common/src/main/java/com/example/ConsumerUtils.java[]
|
|
----
|
|
====
|
|
|
|
`ProducerUtils` contains functions used by the producer.
|
|
The following listing shows the `ProducerUtils` class:
|
|
|
|
====
|
|
[source,java]
|
|
----
|
|
include::{samples_url}/common/src/main/java/com/example/ProducerUtils.java[]
|
|
----
|
|
====
|
|
|
|
[[customization-test-dep]]
|
|
=== Adding a Test Dependency in the Project's Dependencies
|
|
|
|
To add a test dependency in the project's dependencies, you must 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]]
|
|
=== Adding a Test Dependency in the Plugin's Dependencies
|
|
|
|
Now, you must add the dependency for the plugin to reuse at runtime, as the
|
|
following example shows:
|
|
|
|
====
|
|
[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 the following example shows:
|
|
|
|
====
|
|
[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 do NOT have to add the dependency with the extended functionality
|
|
to the consumer side, since the consumer side uses YAML contracts instead of Groovy contracts.
|
|
|
|
[[customization-wiremock]]
|
|
== WireMock Customization
|
|
|
|
In this section, we show 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 similar to
|
|
the following in the `META-INF/spring.factories` file:
|
|
|
|
====
|
|
[source,groovy,indent=0]
|
|
----
|
|
include::{stubrunner_core_path}/src/test/resources/META-INF/spring.factories[indent=0]
|
|
----
|
|
====
|
|
|
|
The following example shows 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: If you want the transformation to be applied only for a mapping that explicitly
|
|
requires it, override the `applyGlobally()` method and set it to `false` .
|
|
|
|
[[customization-wiremock-configuration]]
|
|
=== Customization of WireMock Configuration
|
|
|
|
You can register a bean of type `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer`
|
|
to customize the WireMock configuration (for example, to add custom transformers).
|
|
The following example shows how to do so:
|
|
|
|
====
|
|
[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-wiremock-from-metadata]]
|
|
=== Customization of WireMock via Metadata
|
|
|
|
With version 3.0.0 you're able to set `metadata` in your contracts. If you set an entry with key equal to `wiremock` and the value
|
|
will be a valid WireMock's `StubMapping` JSON / map or an actual `StubMapping` object, Spring Cloud Contract will patch the generated
|
|
stub with part of your customization. Let's look at the following example
|
|
|
|
[source,yaml,indent=0]
|
|
----
|
|
include::{standalone_samples_path}/http-server/src/test/resources/contracts/yml/fraud/shouldReturnFraudStats.yml[tags=metadata,indent=0]
|
|
----
|
|
|
|
In the `metadata` section we've set an entry with key `wiremock` and its value is a JSON `StubMapping` that sets a delay in the generated stub. Such code allowed us to get the following merged WireMock JSON stub.
|
|
|
|
[source,json,indent=0]
|
|
----
|
|
{
|
|
"id" : "ebae49e2-a2a3-490c-a57f-ba28e26b81ea",
|
|
"request" : {
|
|
"url" : "/yamlfrauds",
|
|
"method" : "GET"
|
|
},
|
|
"response" : {
|
|
"status" : 200,
|
|
"body" : "{\"count\":200}",
|
|
"headers" : {
|
|
"Content-Type" : "application/json"
|
|
},
|
|
"fixedDelayMilliseconds" : 2000,
|
|
"transformers" : [ "response-template" ]
|
|
},
|
|
"uuid" : "ebae49e2-a2a3-490c-a57f-ba28e26b81ea"
|
|
}
|
|
----
|
|
|
|
The current implementation allows to manipulate only the stub side (we don't change the generated test). Also, what does not get changed
|
|
are the whole request and body and headers of the response.
|
|
|
|
[[customization-wiremock-from-metadata-custom-processor]]
|
|
==== Customization of WireMock via Metadata and a Custom Processor
|
|
|
|
If you want to apply a custom WireMock `StubMapping` post processing, you can under `META-INF/spring.factories` under the
|
|
`org.springframework.cloud.contract.verifier.converter.StubProcessor` key register your own implementation of a stub processor. For your convenience we've created an interface called `org.springframework.cloud.contract.verifier.wiremock.WireMockStubPostProcessor` that is dedicated to WireMock.
|
|
|
|
You'll have to implement methods to inform Spring Cloud Contract whether the post processor is applicable for a given contract and how should the post processing look like.
|
|
|
|
IMPORTANT: On the consumer side, when using Stub Runner, remember to pass the custom `HttpServerStubConfigurer` implementation (e.g. the one that extends `WireMockHttpServerStubConfigurer`) where you'll register a custom extension of your choosing. If you don't do so, even you have a custom WireMock extension on the classpath, WireMock will not notice it, won't apply it and will print out a warning statement that the given extension was not found.
|
|
|
|
[[customization-pluggable-architecture]]
|
|
== Using the Pluggable Architecture
|
|
|
|
You may encounter cases where 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/java/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/java/org/springframework/cloud/contract/verifier/converter/StubGenerator.java[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 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 the following
|
|
example shows:
|
|
|
|
====
|
|
[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, 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 the following example shows:
|
|
|
|
====
|
|
[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 the following
|
|
example shows:
|
|
|
|
====
|
|
[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, the default (scanning the classpath) is used.
|
|
If you provide the `stubsMode = StubRunnerProperties.StubsMode.LOCAL` or
|
|
`stubsMode = StubRunnerProperties.StubsMode.REMOTE`, the Aether implementation is used
|
|
If you provide more than one, 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 tries
|
|
to clone the repository and use it as a source of contracts
|
|
to generate tests or stubs.
|
|
|
|
Through environment variables, system properties, or properties set
|
|
inside the plugin or the contracts repository configuration, you can
|
|
tweak the downloader's behavior. The following table describes the available
|
|
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 milliseconds to wait between attempts to push the commits to `origin`
|
|
|====
|