How Consumer‑Driven Contract Testing Transforms Distributed Microservice Development with Spring Cloud Contract

This article explains the shortcomings of traditional distributed microservice testing, introduces consumer‑driven contract testing, outlines the evolution of a distributed R&D model, and provides a step‑by‑step guide with code samples for implementing Spring Cloud Contract in both provider and consumer services.

Programmer DD
Programmer DD
Programmer DD
How Consumer‑Driven Contract Testing Transforms Distributed Microservice Development with Spring Cloud Contract

Distributed R&D model and contract testing

In a microservice environment each team owns a service. Bugs are often discovered only during integration testing because the service is first deployed to a test environment. To surface defects earlier, unit tests are added (EasyMock, Mockito) and external dependencies are mocked (Docker containers for DB, Redis). When a provider changes its API, consumers may break, causing production incidents. Consumer‑driven contract testing solves this by defining a contract that both provider and consumer agree on and writing unit tests against it.

Spring Cloud Contract

Spring Cloud Contract (SCC) is used to define these contracts. A contract describes request parameters and expected responses. SCC can generate WireMock stubs, verifier tests and integrates with Spring Cloud.

Used for unit testing

Defines remote service data

Automatically generates test code

Implementation steps

Data definition side

All request/response definitions are placed in a dedicated spring-cloud-contract module (internal name). The contract is written in Groovy DSL; normally the service developer writes it, but a consumer can also author it and have the provider review.

Service provider

Add the following test‑scope Maven dependencies to the provider module:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-wiremock</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-verifier</artifactId>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>com.yonghui</groupId>
    <artifactId>spring-cloud-contract</artifactId>
    <version>1.0‑SNAPSHOT</version>
</dependency>

Configure the spring-cloud-contract-maven-plugin to generate verifier tests and stubs:

<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>1.1.4.RELEASE</version>
    <extensions>true</extensions>
    <configuration>
        <contractsWorkOffline>true</contractsWorkOffline>
        <baseClassMappings>
            <baseClassMapping>
                <contractPackageRegex>.*</contractPackageRegex>
                <baseClassFQN>contract.ContractBase</baseClassFQN>
            </baseClassMapping>
        </baseClassMappings>
        <basePackageForTests>verifier.tests</basePackageForTests>
        <contractDependency>
            <groupId>com.yonghui</groupId>
            <artifactId>spring-cloud-contract</artifactId>
            <version>1.0‑SNAPSHOT</version>
            <classifier>stubs</classifier>
        </contractDependency>
        <contractsPath>contracts/xxx-mst-center</contractsPath>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy</artifactId>
            <version>2.4.12</version>
        </dependency>
        <dependency>
            <groupId>com.yonghui</groupId>
            <artifactId>spring-cloud-contract</artifactId>
            <version>1.0‑SNAPSHOT</version>
            <classifier>stubs</classifier>
        </dependency>
    </dependencies>
</plugin>

Base class for generated tests can initialise an embedded MySQL and load SQL fixtures. Example:

package contract.resources;

import com.yonghui.junit.InmomeryDbResource;

public class LocationDbResource extends InmomeryDbResource {
    public LocationDbResource() {
        super(40200, "xxx_mst_center");
    }

    @Override
    protected void before() throws Throwable {
        super.before();
        runResourceFile(dbName, "sql/contract/mst_location.sql");
    }
}

Bootstrap class used by the provider tests (Consul and other cloud components are disabled to keep the test lightweight):

package com.yonghui.xxx.mst.center.api.impl;

import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

@EnableAutoConfiguration
@Import({FeignConfiguration.class})
@ComponentScan(basePackages = "com.yonghui.xxx")
@MapperScan("com.yonghui.xxx.mst.center.mapper")
public class Bootstrap {
    private static final Logger log = LoggerFactory.getLogger(Bootstrap.class);

    public static void main(String[] args) {
        SpringApplication.run(Bootstrap.class, args);
        log.info("Bootstrap started successfully");
    }
}

Test resources must disable Consul to avoid external service discovery:

spring.cloud.consul.enabled=false
spring.application.feature.enabled=false

Service consumer

The consumer uses the generated stubs via @AutoConfigureStubRunner. The annotation must reference the correct artifact, version and port.

@AutoConfigureStubRunner(
    ids = {"com.yonghui:xxx-mst-center-server:1.0-SNAPSHOT:stubs:5656"},
    workOffline = true)

Example consumer test that starts an embedded MySQL and Redis, loads the Spring context and configures RestAssuredMockMvc:

package contract;

import com.jayway.restassured.module.mockmvc.RestAssuredMockMvc;
import com.yonghui.junit.InmomeryDbResource;
import com.yonghui.junit.RedisResource;
import com.yonghui.xxx.inventory.center.api.impl.TestBootstrap;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.rules.ExternalResource;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestBootstrap.class})
@AutoConfigureStubRunner(
    ids = {"com.yonghui:xxx-mst-center-server:1.0-SNAPSHOT:stubs:5656"},
    workOffline = true)
public class XxxInventoryCenterServiceBase extends InmomeryDbResource {

    @Autowired
    private WebApplicationContext context;

    @ClassRule
    public static final ExternalResource redis = new RedisResource(20300);

    public XxxInventoryCenterServiceBase() {
        super(40200, "xxx_inventory_center");
    }

    @Before
    public void setUp() throws Throwable {
        RestAssuredMockMvc.webAppContextSetup(context);
        super.before();
    }
}

Running mvn clean install -Dmaven.test.skip=false generates the verifier tests under target/generated-test-sources. Use a plugin version greater than 1.1.4.RELEASE to avoid a known DSL long‑type bug.

The three‑party model (data definition project, provider, consumer) centralises contract definitions, enables automatic stub generation, and guarantees that both sides verify the contract during unit testing, reducing integration‑time failures.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaMicroservicesmavenSpring CloudJUnitConsumer-Driven Contracts
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.