By moving from monolithic architectures to Microservices, the complexity gets moved to the architectural level. Teams need to define how their Microservices communicate with one another. Integration tests take a lot of setup time and do not always respond correctly due to changing backend data and wrong behaviour of the involved Microservices.
Consumer Driven Contracts and their benefits
The same way we do testing for each software layer in mocked environments, it is also possible to test boundaries between Microservices with mocks. Consumer Driven Contracts enable teams to design their distributed architecture in TDD style. Spring Cloud Contract offers functionality for contract testing. We have a producer Microservice that offers a REST endpoint to a consumer Microservice that calls the producer’s REST endpoint. In Spring Cloud Contract, the consumer’s team defines the expected request as well as the response of a call to a producer Microservice in the form of a contract. The producer will run these contracts against its endpoints in a test. Upon successful completion of the test, it will generate stubs and publish them to an artefact repository. A stub is a json contract that defines, with regex matchers, the request and response for an endpoint. By using a stub runner in a contract test, the consumer can pull and instantiate these “trusted” stubs as mock of the producer on a random port and instantiate the whole Microservice for the test and verify the generated stubs.
Spring Cloud Contract already offers contract notations in YAML, JSON, and Groovy. However, some companies use popular formats like Swagger to specify their APIs. Maintaining the API specification and CDCs together generates an overhead that we would like to avoid. Therefore, I created the Spring Cloud Contract Swagger converter so Swagger specifications can be used as contracts.
In this example, we define with Swagger the API communication between an app (the consumer) and a Microservice (the producer). The Spring Cloud Contract Swagger project converts our API specification into the Spring Contract format. Spring Cloud Contract does the rest and tests our mocked Microservice as with any other contract format. We can use the generated stub from the producer to test the consumer’s outgoing API calls.
Spring Cloud Contract setup with Swagger
In order to follow this guide, check out the sources from my following GitHub project and look into the projects swagger-coffee-producer-simple and swagger-coffee-consumer.
At first, we need to configure the pom.xml of our producer. We need to define the Spring versions in our properties section. We use version 2.0.0.RELEASE for Spring Cloud Contract, however, your Swagger file extensions must not end with yml or yaml! Otherwise, Spring Cloud Contract will use the YamlConverter instead of the SwaggerContractConverter. If you want to work with yml extensions you can make use of the currently newest snapshot versions and define those in the properties section of your pom.xml.
<spring-cloud.version>Finchley.BUILD-SNAPSHOT</spring-cloud.version> <spring-cloud-contract.version>2.0.1.BUILD-SNAPSHOT</spring-cloud-contract.version> <spring-cloud-contract-swagger.version>1.0.1.RELEASE</spring-cloud-contract-swagger.version>
Next, we have to define the remote repositories to pull from the artefacts and our dependency management. You can get the configuration on the Spring Cloud Contract page.
We add Spring Cloud Contract Verify as dependency so we can make use of REST Assured. Our tests will run without a servlet container and therefore execute faster.
To convert our Swagger file into a contract and to execute it against our Producer, we add the spring-cloud-contract-maven-plugin as plugin and the converter spring-cloud-contract-swagger as plugin-dependency. In the configuration blog, we specify the path to the base class, which will contain our mock configuration.
We execute our base with the MockitoJUnitRunner and use REST Assured to mock our controller.
When executing the tests with mvn verify, Spring Cloud Contract will generate a contract test that inherits our base class and contains data from the contract. It will also generate a stub in json format. With mvn install or mvn deploy, we publish these stubs and our Swagger files in a stub jar for the consumer.
Similar to the producer, we need to configure properties, remote repositories, and dependency management.
We add as dependencies the spring-cloud-starter-contract-stub-runner and the converter spring-cloud-contract-swagger.
We write tests that execute against endpoints of the consumer and mock the producer with MockMVC. Our consumer will be started in a mock servlet environment. In @AutoConfigureStubRunner, we specify the stubs mode, e.g. LOCAL, and the artefact that contains our stubs and Swagger files. Via @StubRunnerPort we can inject the port of our running stub and configure our consumer to run its requests against this port.
The following setups allow the team of the consumer and producer to work simultaneously. The consumer team does not need to wait for the base class and API implementation of the producer team.
Preferably, we would define a Swagger specification in a separate Git repository. Both teams of the consumer and producer should have access to this repository.
In Spring Cloud Contract samples Git repository, when executing mvn verify, the stubs are generated and moved next to the contracts, into the mappings folder. You could define a Jenkins Job DSL for executing mvn verify and for pushing your stubs to Git with each commit.
As an example, look at the producer swagger-coffee-producer-git and the consumer swagger-coffee-consumer/MissionLaunchControllerGitTest.
I recommend this setup with Maven artefacts since you have better control of versioning with Maven and Spring Cloud Contract. In swagger-coffee-contracts, we store our Swagger specifications in a custom folder structure. We can generate the stubs and deploy these to our artefact repository.
The producer swagger-coffee-producer-external defines the swagger-coffee-contracts artefact as contract dependency in the spring-cloud-contract-maven-plugin’s configuration.
The consumer swagger-coffee-consumer/MissionLaunchControllerExternalTest defines – like any other consumer – the external contracts as stubs to run in its test.
Configuration of Swagger Values
Currently, Spring Cloud Contract Swagger generates default values for a Swagger document’s fields.
- Boolean: true
- Number: 1
- Float/Double: 1.1
- String: (the name of the String)
To set your own default values, you can use the x-example field in the parameters and responses section. In the definitions section, you can also use the x-example or the supported example field. You should avoid the default field, since the current Swagger parser (1.0.36) interprets numerical values of default fields as String. Regarding the order, the converter will first evaluate example, then x-example, and then default fields. If it does not find a predefined value, it will go all the way down to the primitive fields. The converter will only use the first response of a Swagger method entry.
Ignoring Swagger Resources
In each get, post, put, delete section you can add a x-ignore property with value true so the Swagger converter will skip this resource. This allows you to already define a resource without breaking the contract tests of a producer. However, you should keep track of versioning your contract artefacts.
Guides & More
- Spring Cloud Contract samples Git repository
- Spring Cloud Contract Swagger Sample
- Spring Cloud Contract Swagger
- [Grz18] Marcin Grzejszczak (2018). Spring Cloud Contract in a polyglot world. Retrieved from:
- [Mor17] Andrew Morgan (2017). Q&A with Marcin Grzejszczak on Spring Cloud Contract. Retrieved from: