A practical introduction to Spring Cloud Contract


A practical introduction to Spring Cloud Contract

Microservices are eating the world! The arrival of this concept changed not only the way we’re designing our software architecture, but also how teams are formed, how they’re organized and how they work together.

One of the many other challenges that Microservices brings, is the way we test changes made on them. Martin Fowler
and James Lewis
Introduced on their definition of Microservices
the concept of Consumer-Driven Contract Testing:

Executing consumer driven contracts as part of your build increases confidence and provides fast feedback on whether your services are functioning.

In this quick post, we’ll briefly define the concept of CDC, as well as testing a Producer and consumer communicating throught HTTP, using Spring Cloud Contract.

CDC Testing

Consumer Driven Contract approach is nothing more than an agreement, to test integration points, between the Server (Consumer) and Client (Provider) about the format of data that they communicate between each other, eliminating the hassle of end to end tests.

Spring Cloud Contract
is an amazing framework that facilitates consumer driven contract tests.

Show me the code

Server / Producer side

First we need to add spring-cloud-starter-contract-verifie
to our Producer pom and configure the spring-cloud-contract-maven-plugin
with the base class for tests, which I will describe a bit later.




Our simple producer is in the form of BookController
, that exposes HTTP REST APIs for managing books.

public class BookController {

    BookService bookService;

            method = RequestMethod.POST,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Book createBook(@RequestBody Book book) {
         return bookService.createNew(book);

    @RequestMapping(value = "/{isbn}",
            method = RequestMethod.PUT,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Book updateBook(@PathVariable String isbn, @RequestBody Book book) {

        return bookService.update(isbn,book);

    @RequestMapping(value = "/{isbn}",
            method = RequestMethod.GET,
            produces = MediaType.APPLICATION_JSON_VALUE)
    public Optional getByIsbn(@PathVariable String isbn) {
        return bookService.findByIsbn(isbn);


To verify that the above controller really obeys the contract, We need necessarily to create a base test which is subclassed by all later generated tests:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class BookApiBase {

    BookController bookController;

    private BookRepository repository;

    public void setup() {
        Book book= new Book("123", "Ferok Book", "Fero Hero");

        StandaloneMockMvcBuilder standaloneMockMvcBuilder = MockMvcBuilders.standaloneSetup(bookController);

In this base class, we’re setting up a Spring Boot application with @SpringBootTest and mocking away the BookRepository
so that it always returns a book (the one specified in the contract). Then, we set up RestAssured
so that the generated tests can simply use RestAssured
to send requests against our controller.

Spring Cloud Contract automatically (and magically) generates JUnit tests from a given contract. A simple contract would look something like below:

name: "should Create a book"  
  method: POST
  url: /api/books
    isbn: "123"
    author: "Fero Hero"
    title: "Ferok Book"
    Content-Type: application/json
  status: 201
    isbn: "123"
    author: "Fero Hero"
    title: "Ferok Book"
    Content-Type: application/json

Each contract defines a single request
/ response
pair. The contract above defines an API that consists of a POST request to the URL /api/books
containing some data in the body and an expected response to that request, returning HTTP code 201 and the newly created book as body.

Note that I’ve used a YAML file to define my contract, But groovy DSL
is also supported in Spring Cloud Contract. The contract files are expected to be located under src/test/resources/contracts

When we run the build, the plugin (i.e spring-cloud-contract-maven-plugin
) automatically generates a test class named ContractVerifierTest
that extends our BookApiBase
and puts it under /target/generated-test-sources/contracts/
. The build will also add the stub jar in our local Maven repository so that it can be used by our consumer.

The names of the test methods are derived from the prefix validate_
concatenated with the names of our YAML test stubs. For the above YAML file, the generated method name will be validate_should_Create_a_book

public class ContractVerifierTest extends BookApiBase {

    public void validate_should_Create_a_book() throws Exception {
        // given:
            MockMvcRequestSpecification request = given()
                    .header("Content-Type", "application/json")
                    .body("{"isbn":"123","author":"Fero Hero","title":"Ferok Book"}");

        // when:
            ResponseOptions response = given().spec(request)

        // then:
        // and:
            DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
            assertThatJson(parsedJson).field("['title']").isEqualTo("Ferok Book");
            assertThatJson(parsedJson).field("['author']").isEqualTo("Fero Hero");



Client / Consumer Side

On the Client side, we have to write our tests and make some configurations to execute it against the Provider Stub to maintain the contract, so any changes on the producer side would break the contract.

My consumer is simply a

which will make an HTTP request to get the response from the generated stubs:

public interface BookClient {

    @RequestMapping(method = RequestMethod.GET, path = "/api/books/{isbn}", consumes = MediaType.APPLICATION_JSON_VALUE)
    Book getBook(@PathVariable("isbn") String isbn);

    @RequestMapping(method = RequestMethod.POST, path = "/api/books", consumes = MediaType.APPLICATION_JSON_VALUE)
    Book createBook(@RequestBody Book book);


We’ll need also to add spring-cloud-starter-contract-stub-runner
dependency to our consumer:


The last step is to setup the Stub Runner
in our tests to automatically download the required stubs. To achieve that we have to pass the @AutoConfigureStubRunner

        ids = "me.aboullaite.spring.cloud:spring-cloud-contract-producer:+:stubs:9090",
        stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class BookClientTest {

    private BookClient bookClient;

    public void getBookByisbnCompliesToContract() {
        Book book = bookClient.getBook("123");

    public void createBookCompliesToContract() {
        Book book= new Book("123", "Ferok Book", "Fero Hero");
        Book createdBook = bookClient.createBook(book);

For our example, the ids property of the @AutoConfigureStubRunner
annotation specifies:

  • me.aboullaite.spring.cloud
    : the groupId of our artifact
  • spring-cloud-contract-producer
    — the artifactId of the producer stub jar
  • 9090
    — the port on which the generated stubs will run

Since we fixed the stubsMode
option to LOCAL
then the stubs will be downloaded from our local Maven repo. Once the test context got booted up, Spring Cloud Contract Stub Runner will automatically start a WireMock server inside our test and feed it with the stubs generated from the server side.

That’s all folks! A more complete version of the code used is available on gitlab.


A practical introduction to Spring Cloud Contract

If Android is nixed, Huawei’s backup OS will not be as robust


黑猫投诉携微博发起黑猫挑战 王丽坤等引领关注消费



A practical introduction to Spring Cloud Contract