user-icon Antoniya Atanasova
13. June 2017
timer-icon 6 min

Consumer Driven Contracts with Spring Cloud Contract

In our previous blog post, we have already introduced the topic of Microservices Consumer Driven Contract (CDC) Testing and the motivation behind it. We also took a look at PACT as a CDC testing framework by providing a getting started Spring boot based example. In the current blog post we will focus our attention on Spring Cloud Contract framework for CDC testing. The Spring Cloud Contract Project itself consists of three main components – Spring Cloud Contract Verifier, Spring Cloud Contract WireMock and Spring Cloud Contract RestDocs. Since this blogpost cannot cover all the possible combinations of technologies and options that Spring Cloud Contract provides, our major focus will be on the Spring Cloud Contract Verifier. However, just for curiousity, we have added two test samples – one for Spring WireMock and the other with Spring RestDocs in our demo project, just to get an idea. Further details, you can find on the official documentation.

Let’s get started from our Github project: Spring Clould Contract Demo Github Repo

Let’s first start by introducing our case. Suppose that we have two services (see Figure 1) – Consumer and Provider that communicate between each other via REST. The Provider or Product service in our case provides all product relevant information such as nametype and description, by a given product identification number. The Consumer, on the other hand, fetches the data and consumes the relevant information.
Consumer-Provider communication

When it comes to contract testing, we start by defining our first contract that contains the expectation of the API we are using. As a rule of thumb the contract has to be defined by the Consumer service, however in Spring Cloud Contract it is physically located in the Provider service code. One approach as advised in the official documentation is to clone the Provider service project and have it locally for writing the contract. The guide consists of two major steps that contain smaller steps. The first step focuses on what should be done on the Provider side and the second step groups the activities on the Consumer side.

Step 1 Actions Overview

1.1. Write the contract specification (Groovy DSL)
1.2. Generate automated acceptance tests on Provider side
1.3. Generate WireMock JSON stub & Publish the stub to Maven (local) repository

1.1. Write the contract specification (Groovy DSL)

The contract is written in Groovy DSL and consists of two main parts – defining the request and expected response. By specification, there is a minimum of elements that have to be defined. For the Request part those are: method and urlPath, for the response part: status code. Detailed information about the way they are defined as well as the Groovy DSL syntax specifics can be found here – https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_contract_dsl. In the following code block, you can see the defined contract for our Product demo case. The default location for specifying a contract is in /test/resources/contracts.

1.2. Generate automated acceptance tests on Provider side

When you are on the Provider side, you can use the possibilities of spring-cloud-contract-verifier to automatically generate a test based on this contract that verifies the Provider’s implementation. Before that you need to have a base class implemented that all of the generated tests will extend from. In our example this is the ContractVerifierBaseTest.java, which has to be configured in the build.gradle file (the full build.gradle example is available in the GitHub repository).


When you execute “gradle generateContractTests” from the provider project, the generated tests have to be located under /build/generated-test-sources. The following code block shows how exactly the test looks like. Its name is formed with the prefix validate_ the name of the contract file (= validate_productContractDemo). Given that the ProductController request mapping is already implemented and you execute the test, it is expected to pass. However, as it is more likely the case, if you have not yet implemented the logic of your RestController, then the test will fail.

1.3. Generate WireMock JSON stub & Publish the stub to Maven (local) repository

Once the test passes and the Provider service-side necessary logic is implemented, we can move on to the next step – generating and installing stubs from the Provider. But before we do that on a technical level, let us have a look at why those stubs are necessary. On the consumer we do not want to execute our tests against a real deployed Provider service. On the other hand, we do not want to use an out of date mocked Provider service as already discussed in our previous blog post under Intergration tests. Therefore, Spring Cloud Contract provides us with the functionality to use a stubbed Provider for our Consumer tests, that is at the same time up to date with the real Provider. This is what we actually did in the step so far – we have created a contract, generated an automated test that is executed against the real Provider implementation. Once it is green, we can generate the Provider Stub and pass it on to the Consumer.

We do that by executing gradle clean build installThis command does two steps for us.

  • Firstly, it generates the WireMock (WireMock is a framework for service virtualization) JSON stub definitions (under /build/stubs/mappings).
  • Secondly, it publishes those stubs into the Maven local repository. The jar file contains the contract and the stub.

Consumer with stubbed Provider

Step 2 Actions Overview

1.1. Configure Stub Runner on the Consumer side
1.2. Execute the Consumer test – Stub Runner has embedded WireMock
1.3. Check verification results

1.1. Configure Stub Runner on the Consumer side

On the Consumer side, we have to write our test and make configurations to execute it against the Provider Stub. Ideally, we will have the tests written before creating the contract with the Provider. The ConsumerDemoTest.java does nothing more than executing the request for fetching Product data based on a product id. At the end it asserts that the received data is as expected. What is more interesting is this part of the configuration:


Using this line we make the configuration of spring-cloud-starter-contract-stub-runner. WorkOffline equals true, means that the stub runner will search for the Provider stub in the Maven local repository. With the value of ids, we say to use the latest installed stubs version from the Maven repo with groupId = info.novatec.spring.contract.cloud.example, artifcatId = provider-service and specify a port for the test. The Stub runner comes with an embedded WireMock, thus when starting your tests the Stub runner will start a WireMock server internally and install on it the Provider stubs.

1.2. Execute the Consumer test & check verification results

After that it will execute the test logic and finally will do the assertions. If everything has been properly set, the test is expected to pass. Below you can see a short info from the test execution log that proves the stub installation and running process. If an error has been found then of course the team working on Consumer service has to communicate with the Provider’s service team.

Summary

In this blog post we presented the second framework supporting the concept behind CDC – Spring Cloud Contract. It is only suitable for JVM based applications. PACT, on the other hand, provides a wider range of programming language support, which could be a factor when we have microservices and at least one of them is not Java based. Spring Cloud Contract is more platform-specific, but provides further functionalities such as the stub runner, and the automatic test generation on the Provider side. Whether it should be a first choice for Java, is a matter of project context, needs and personal choice.

An interesting consequence behind the idea of CDC is that it actually promotes the concept of Test Driven Development on the architectural level. You start by implementing a test on the consumer side that initially fails because there is no provider yet. Then you write/generate the contract with the Provider. The Provider, then, can generate tests out of this contract i.e. the requirements that it needs to fulfil. At first, the test will fail. After implementing the API logic, it will (hopefully) pass. At the end the consumer can also run the tests against the stubbed Provider and make them pass.

Comment article

Comments

  1. Antoniya Atanasova

    Hi Hakki,

    Thanks for your interest. Sure – there is another blog post that shows what the functionalities of PACT are: https://blog.novatec-gmbh.de/introduction-microservices-testing-consumer-driven-contract-testing-pact/. Which of the frameworks you are going to choose is a question that depends on your project context, team set up and therefore not easy to answer in a blog post. You can also use the demo projects as basis for further evaluation 🙂

    Thanks,
    Antoniya

  2. Hakki Bagci

    Hi Antoniya!
    Thanks for the two nice blog posts about CDC. Is it possible that you elaborate more on the differences between Pact and Spring Cloud Contract? For instance, what is possible in one and not in other? Which is easier to integrate with continuous delivery systems. Should Pact still be an option to consider for the projects implemented using Spring Cloud or should Spring Cloud Contract be the default choice for these type of projects? I would like to hear your thoughts about these.

  3. Antoniya Atanasova

    Hi Marcin!

    Thank you for your feedback! I really found the framework great!

  4. Marcin Grzejszczak

    Hi Antoniya!

    This is a great post! Congrats 🙂 Glad that you enjoyed working with Spring Cloud Contract.

    > In this blog post we presented the second framework supporting the concept behind CDC – Spring Cloud Contract. It is only suitable for JVM based applications.

    Not necessarily. I’d say that it’s more suited for JVM based applications. When developing a non JVM basde application, if you’re the consumer, then you can run the Stub Runner Boot application – https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_stub_runner_boot_application just by typing `java -jar …` . That way you’ll automatically download the stubs and run them aside of your process.