You can set up Spring Cloud Contract Verifier in either of two ways
To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the following sections:
In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a Gradle or a Maven plugin.
Warning | |
---|---|
If you want to use Spock in your projects, you must add separately the
|
To add a Gradle plugin with dependencies, use code similar to this:
buildscript { repositories { mavenCentral() } dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}" } } apply plugin: 'groovy' apply plugin: 'spring-cloud-contract' dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${verifier_version}" } } dependencies { testCompile 'org.codehaus.groovy:groovy-all:2.4.6' // example with adding Spock core and Spock Spring testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4' testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier' }
By default, Rest Assured 3.x is added to the classpath. However, to use Rest Assured 2.x you can add it to the plugins classpath, as shown here:
buildscript { repositories { mavenCentral() } dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}" classpath "com.jayway.restassured:rest-assured:2.5.0" classpath "com.jayway.restassured:spring-mock-mvc:2.5.0" } } depenendencies { // all dependencies // you can exclude rest-assured from spring-cloud-contract-verifier testCompile "com.jayway.restassured:rest-assured:2.5.0" testCompile "com.jayway.restassured:spring-mock-mvc:2.5.0" }
That way, the plugin automatically sees that Rest Assured 2.x is present on the classpath and modifies the imports accordingly.
Add the additional snapshot repository to your build.gradle to use snapshot versions, which are automatically uploaded after every successful build, as shown here:
buildscript { repositories { mavenCentral() mavenLocal() maven { url "http://repo.spring.io/snapshot" } maven { url "http://repo.spring.io/milestone" } maven { url "http://repo.spring.io/release" } } }
By default, Spring Cloud Contract Verifier is looking for stubs in the
src/test/resources/contracts
directory.
The directory containing stub definitions is treated as a class name, and each stub definition is treated as a single test. Spring Cloud Contract Verifier assumes that it contains at least one level of directories that are to be used as the test class name. If more than one level of nested directories is present, all except the last one is used as the package name. For example, with following structure:
src/test/resources/contracts/myservice/shouldCreateUser.groovy src/test/resources/contracts/myservice/shouldReturnUser.groovy
Spring Cloud Contract Verifier creates a test class named defaultBasePackage.MyService
with two methods:
shouldCreateUser()
shouldReturnUser()
The plugin registers itself to be invoked before a check
task. If you want it to be
part of your build process, you need to do nothing more. If you just want to generate
tests, invoke the generateContractTests
task.
The default Gradle Plugin setup creates the following Gradle part of the build (in pseudocode):
contracts { targetFramework = 'JUNIT' testMode = 'MockMvc' generatedTestSourcesDir = project.file("${project.buildDir}/generated-test-sources/contracts") contractsDslDir = "${project.rootDir}/src/test/resources/contracts" basePackageForTests = 'org.springframework.cloud.verifier.tests' stubsOutputDir = project.file("${project.buildDir}/stubs") // the following properties are used when you want to provide where the JAR with contract lays contractDependency { stringNotation = '' } contractsPath = '' contractsWorkOffline = false contractRepository { cacheDownloadedContracts(true) } } tasks.create(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') { baseName = project.name classifier = contracts.stubsSuffix from contractVerifier.stubsOutputDir } project.artifacts { archives task } tasks.create(type: Copy, name: 'copyContracts') { from contracts.contractsDslDir into contracts.stubsOutputDir } verifierStubsJar.dependsOn 'copyContracts' publishing { publications { stubs(MavenPublication) { artifactId project.name artifact verifierStubsJar } } }
To change the default configuration, add a contracts
snippet to your Gradle config, as
shown here:
contracts { testMode = 'MockMvc' baseClassForTests = 'org.mycompany.tests' generatedTestSourcesDir = project.file('src/generatedContract') }
baseClassForTests’s package and from `packageWithBaseClasses
.
If neither of these values are set, then the value is set to
org.springframework.cloud.contract.verifier.tests
.spock.lang.Specification
.Antmatcher
to allow defining stub files for which processing
should be skipped. By default, it is an empty array.$rootDir/src/test/resources/contracts
.$buildDir/generated-test-sources/contractVerifier
.The following properties are used when you want to specify the location of the JAR
containing the contracts:
* contractDependency: Specifies the Dependency that provides
groupid:artifactid:version:classifier
coordinates. You can use the contractDependency
closure to set it up.
* contractsPath: Specifies the path to the jar. If contract dependencies are
downloaded, the path defaults to groupid/artifactid
where groupid
is slash
separated. Otherwise, it scans contracts under the provided directory.
* contractsWorkOffline: Specifies whether to download the dependencies each time, so
that you can work online. In other words, it specifies whether to reuses the local Maven
repo.
When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an endpoint, which should be verified.
abstract class BaseMockMvcSpec extends Specification { def setup() { RestAssuredMockMvc.standaloneSetup(new PairIdController()) } void isProperCorrelationId(Integer correlationId) { assert correlationId == 123456 } void isEmpty(String value) { assert value == null } }
If you use Explicit
mode, you can use a base class to initialize the whole tested app
as you might see in regular integration tests. If you use the JAXRSCLIENT
mode, this
base class should also contain a protected WebTarget webTarget
field. Right now, the
only option to test the JAX-RS API is to start a web server.
If your base classes differ between contracts, you can tell the Spring Cloud Contract plugin which class should get extended by the autogenerated tests. You have two options:
packageWithBaseClasses
baseClassMappings
By Convention
The convention is such that if you have a contract under (for example)
src/test/resources/contract/foo/bar/baz/
and set the value of the
packageWithBaseClasses
property to com.example.base
, then Spring Cloud Contract
Verifier assumes that there is a BarBazBase
class under the com.example.base
package.
In other words, the system takes the last two parts of the package, if they exist, and
forms a class with a Base
suffix. This rule takes precedence over baseClassForTests.
Here is an example of how it works in the contracts
closure:
packageWithBaseClasses = 'com.example.base'
By Mapping
You can manually map a regular expression of the contract’s package to fully qualified
name of the base class for the matched contract. You have to provide a list called
baseClassMappings
that consists baseClassMapping
objects that takes a
contractPackageRegex
to baseClassFQN
mapping. Consider the following example:
baseClassForTests = "com.example.FooBase" baseClassMappings { baseClassMapping('.*/com/.*', 'com.example.ComBase') baseClassMapping('.*/bar/.*':'com.example.BarBase') }
Let’s assume that you have contracts under
- src/test/resources/contract/com/
- src/test/resources/contract/foo/
By providing the baseClassForTests
, we have a fallback in case mapping did not succeed.
(You could also provide the packageWithBaseClasses
as a fallback.) That way, the tests
generated from src/test/resources/contract/com/
contracts extend the
com.example.ComBase
, whereas the rest of the tests extend com.example.FooBase
.
To ensure that the provider side is compliant with defined contracts, you need to invoke:
./gradlew generateContractTests test
In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin
in exactly the same way as in case of provider. If you do not want to use Stub Runner
then you need to copy contracts stored in src/test/resources/contracts
and generate
WireMock JSON stubs using:
./gradlew generateClientStubs
Note | |
---|---|
The |
When present, JSON stubs can be used in automated tests of consuming a service.
@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application) class LoanApplicationServiceSpec extends Specification { @ClassRule @Shared WireMockClassRule wireMockRule == new WireMockClassRule() @Autowired LoanApplicationService sut def 'should successfully apply for loan'() { given: LoanApplication application = new LoanApplication(client: new Client(clientPesel: '12345678901'), amount: 123.123) when: LoanApplicationResult loanApplication == sut.loanApplication(application) then: loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED loanApplication.rejectionReason == null } }
LoanApplication
makes a call to FraudDetection
service. This request is handled by a
WireMock server configured with stubs generated by Spring Cloud Contract Verifier.
To learn how to set up the Maven project for Spring Cloud Contract Verifier, read the following sections:
Add the Spring Cloud Contract BOM in a fashion similar to this:
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud-dependencies.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Next, add the Spring Cloud Contract Verifier
Maven plugin:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> <extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example.fraud</packageWithBaseClasses> </configuration> </plugin>
You can read more in the Spring Cloud Contract Maven Plugin Documentation.
By default, Rest Assured 3.x is added to the classpath. However, you can use Rest Assured 2.x by adding it to the plugins classpath, as shown here:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> <extensions>true</extensions> <configuration> <packageWithBaseClasses>com.example</packageWithBaseClasses> </configuration> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-verifier</artifactId> <version>${spring-cloud-contract.version}</version> </dependency> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>rest-assured</artifactId> <version>2.5.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>spring-mock-mvc</artifactId> <version>2.5.0</version> <scope>compile</scope> </dependency> </dependencies> </plugin> <dependencies> <!-- all dependencies --> <!-- you can exclude rest-assured from spring-cloud-contract-verifier --> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>rest-assured</artifactId> <version>2.5.0</version> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.restassured</groupId> <artifactId>spring-mock-mvc</artifactId> <version>2.5.0</version> <scope>test</scope> </dependency> </dependencies>
That way, the plugin automatically sees that Rest Assured 3.x is present on the classpath and modifies the imports accordingly.
For Snapshot and Milestone versions, you have to add the following section to your
pom.xml
, as shown here:
<repositories> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> <pluginRepository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> </pluginRepositories>
By default, Spring Cloud Contract Verifier is looking for stubs in the
src/test/resources/contracts
directory. The directory containing stub definitions is
treated as a class name, and each stub definition is treated as a single test. We assume
that it contains at least one directory to be used as test class name. If there is more
than one level of nested directories, all except the last one is used as package name.
For example, with following structure:
src/test/resources/contracts/myservice/shouldCreateUser.groovy src/test/resources/contracts/myservice/shouldReturnUser.groovy
Spring Cloud Contract Verifier creates a test class named defaultBasePackage.MyService
with two methods
shouldCreateUser()
shouldReturnUser()
The plugin goal generateTests
is assigned to be invoked in the phase called
generate-test-sources
. If you want it to be part of your build process, you need not do
anything. If you just want to generate tests, invoke the generateTests
goal.
To change the default configuration, just add a configuration
section to the plugin
definition or the execution
definition, as shown here:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <executions> <execution> <goals> <goal>convert</goal> <goal>generateStubs</goal> <goal>generateTests</goal> </goals> </execution> </executions> <configuration> <basePackageForTests>org.springframework.cloud.verifier.twitter.place</basePackageForTests> <baseClassForTests>org.springframework.cloud.verifier.twitter.place.BaseMockMvcSpec</baseClassForTests> </configuration> </plugin>
baseClassForTests’s package and from `packageWithBaseClasses
.
If neither of these values are set, then the value is set to
org.springframework.cloud.contract.verifier.tests
.spock.lang.Specification
./src/test/resources/contracts
.src/test/resources/contract/foo/bar/baz/
and set
the value of the packageWithBaseClasses
property to com.example.base
, then Spring
Cloud Contract Verifier assumes that there is a BarBazBase
class under the
com.example.base
package. In other words, the system takes the last two parts of the
package, if they exist, and forms a class with a Base
suffix.contractPackageRegex
, which is checked against the package where the contract is
located, and baseClassFQN
, which maps to the fully qualified name of the base class for
the matched contract. For example, if you have a contract under
src/test/resources/contract/foo/bar/baz/
and map the property
.* → com.example.base.BaseClass
, then the test class generated from these contracts
extends com.example.base.BaseClass
. This setting takes precedence over
packageWithBaseClasses and baseClassForTests.If you want to download your contract definitions from a Maven repository, you can use the following options:
groupid/artifactid
where gropuid
is slash separated.We cache only non-snapshot, explicitly provided versions (for example
+
or 1.0.0.BUILD-SNAPSHOT
won’t get cached). By default, this feature is turned on.
When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base specification for all generated acceptance tests. In this class, you need to point to an endpoint, which should be verified.
package org.mycompany.tests import org.mycompany.ExampleSpringController import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc import spock.lang.Specification class MvcSpec extends Specification { def setup() { RestAssuredMockMvc.standaloneSetup(new ExampleSpringController()) } }
If you use Explicit
mode, you can use a base class to initialize the whole tested app
similarly, as you might find in regular integration tests. If you use the JAXRSCLIENT
mode, this base class should also contain a protected WebTarget webTarget
field. Right
now, the only option to test the JAX-RS API is to start a web server.
If your base classes differ between contracts, you can tell the Spring Cloud Contract plugin which class should get extended by the autogenerated tests. You have two options:
packageWithBaseClasses
baseClassMappings
By Convention
The convention is such that if you have a contract under (for example)
src/test/resources/contract/foo/bar/baz/
and set the value of the
packageWithBaseClasses
property to com.example.base
, then Spring Cloud Contract
Verifier assumes that there is a BarBazBase
class under the com.example.base
package.
In other words, the system takes the last two parts of the package, if they exist, and
forms a class with a Base
suffix. This rule takes precedence over baseClassForTests.
Here is an example of how it works in the contracts
closure:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <configuration> <packageWithBaseClasses>hello</packageWithBaseClasses> </configuration> </plugin>
By Mapping
You can manually map a regular expression of the contract’s package to fully qualified
name of the base class for the matched contract. You have to provide a list called
baseClassMappings
that consists baseClassMapping
objects that takes a
contractPackageRegex
to baseClassFQN
mapping. Consider the following example:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <configuration> <baseClassForTests>com.example.FooBase</baseClassForTests> <baseClassMappings> <baseClassMapping> <contractPackageRegex>.*com.*</contractPackageRegex> <baseClassFQN>com.example.TestBase</baseClassFQN> </baseClassMapping> </baseClassMappings> </configuration> </plugin>
Assume that you have contracts under these two locations:
* src/test/resources/contract/com/
* src/test/resources/contract/foo/
By providing the baseClassForTests
, we have a fallback in case mapping did not succeed.
(You can also provide the packageWithBaseClasses
as a fallback.) That way, the tests
generated from src/test/resources/contract/com/
contracts extend the
com.example.ComBase
, whereas the rest of the tests extend com.example.FooBase
.
The Spring Cloud Contract Maven Plugin generates verification code in a directory called
/generated-test-sources/contractVerifier
and attaches this directory to testCompile
goal.
For Groovy Spock code, use the following:
<plugin> <groupId>org.codehaus.gmavenplus</groupId> <artifactId>gmavenplus-plugin</artifactId> <version>1.5</version> <executions> <execution> <goals> <goal>testCompile</goal> </goals> </execution> </executions> <configuration> <testSources> <testSource> <directory>${project.basedir}/src/test/groovy</directory> <includes> <include>**/*.groovy</include> </includes> </testSource> <testSource> <directory>${project.build.directory}/generated-test-sources/contractVerifier</directory> <includes> <include>**/*.groovy</include> </includes> </testSource> </testSources> </configuration> </plugin>
To ensure that provider side is compliant with defined contracts, you need to invoke
mvn generateTest test
.
If you see the following exception while using STS:
When you click on the error marker you should see something like this:
plugin:1.1.0.M1:convert:default-convert:process-test-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring- cloud-contract-maven-plugin:1.1.0.M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:145) at org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:331) at org.eclipse.m2e.core.internal.embedder.MavenImpl$11.call(MavenImpl.java:1362) at ... org.eclipse.core.internal.jobs.Worker.run(Worker.java:55) Caused by: java.lang.NullPointerException at org.eclipse.m2e.core.internal.builder.plexusbuildapi.EclipseIncrementalBuildContext.hasDelta(EclipseIncrementalBuildContext.java:53) at org.sonatype.plexus.build.incremental.ThreadBuildContext.hasDelta(ThreadBuildContext.java:59) at
In order to fix this issue, provide the following section in your pom.xml
:
<build> <pluginManagement> <plugins> <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --> <plugin> <groupId>org.eclipse.m2e</groupId> <artifactId>lifecycle-mapping</artifactId> <version>1.0.0</version> <configuration> <lifecycleMappingMetadata> <pluginExecutions> <pluginExecution> <pluginExecutionFilter> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <versionRange>[1.0,)</versionRange> <goals> <goal>convert</goal> </goals> </pluginExecutionFilter> <action> <execute /> </action> </pluginExecution> </pluginExecutions> </lifecycleMappingMetadata> </configuration> </plugin> </plugins> </pluginManagement> </build>
You can also use the Spring Cloud Contract Verifier for the consumer side. To do so, use
the plugin so that it only converts the contracts and generates the stubs. To achieve
that, you need to configure Spring Cloud Contract Verifier plugin in exactly the same way
as you would for a provider. You need to copy contracts stored in
src/test/resources/contracts
and generate WireMock JSON stubs using the
mvn generateStubs
command. By default, the generated WireMock mapping is stored in a
directory named target/mappings
. From these generated mappings, your project should
create additional artifacts with a classifier of stubs
for easy deployment to the maven
repository.
Here is a sample configuration:
<plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${verifier-plugin.version}</version> <executions> <execution> <goals> <goal>convert</goal> <goal>generateStubs</goal> </goals> </execution> </executions> </plugin>
When present, JSON stubs can be used in consumer automated tests, as shown here:
@RunWith(SpringTestRunner.class) @SpringBootTest @AutoConfigureStubRunner public class LoanApplicationServiceTests { @Autowired LoanApplicationService service; @Test public void shouldSuccessfullyApplyForLoan() { //given: LoanApplication application = new LoanApplication(new Client("12345678901"), 123.123); //when: LoanApplicationResult loanApplication = service.loanApplication(application); // then: assertThat(loanApplication.loanApplicationStatus).isEqualTo(LoanApplicationStatus.LOAN_APPLIED); assertThat(loanApplication.rejectionReason).isNull(); } }
LoanApplication
makes a call to the FraudDetection
service. This request is handled
by a WireMock server configured with stubs generated by the Spring Cloud Contract
Verifier.
The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One problem that arises is that, when reusing the stubs, you can mistakenly import all of that stub’s dependencies. When building a Maven artifact, even though you have a couple of different jars, all of them share one pom:
├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar ├── github-webhook-0.0.1.BUILD-20160903.075506-1-stubs.jar.sha1 ├── github-webhook-0.0.1.BUILD-20160903.075655-2-stubs.jar ├── github-webhook-0.0.1.BUILD-20160903.075655-2-stubs.jar.sha1 ├── github-webhook-0.0.1.BUILD-SNAPSHOT.jar ├── github-webhook-0.0.1.BUILD-SNAPSHOT.pom ├── github-webhook-0.0.1.BUILD-SNAPSHOT-stubs.jar ├── ... └── ...
There are three possibilities of working with those dependencies so as not to have any issues with transitive dependencies:
Mark all application dependencies as optional
If, in the github-webhook
application, you mark all of your dependencies as optional,
when you include the github-webhook
stubs in another application (or when that
dependency gets downloaded by Stub Runner) then, since all of the dependencies are
optional, they will not get downloaded.
Create a separate artifactid
for the stubs
If you create a separate artifactid
, then you can set it up in whatever way you wish.
For example, you might decide to have no dependencies at all.
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.
You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to stick to the proper naming convention while creating your contracts. The convention requires including an order number followed by an underscore, as shown in this example:
my_contracts_dir\ scenario1\ 1_login.groovy 2_showCart.groovy 3_logout.groovy
Such a tree causes Spring Cloud Contract Verifier to generate WireMock’s scenario with a
name of scenario1
and the three following steps:
Started
pointing to…Step1
pointing to…Step2
which will close the scenario.More details about WireMock scenarios can be found at http://wiremock.org/stateful-behaviour.html
Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.