Contract Testing with Spring Cloud Contract in Spring Boot

In modern microservices architecture, ensuring that service integrations are reliable and well-tested is critical. Traditional integration tests between services can be slow, flaky, and hard to maintain.

Contract Testing with Spring Cloud Contract solves this by allowing consumers and providers to agree on a shared contract. The contract is verified automatically on both sides, ensuring that:

  • Consumers get what they expect
  • Providers deliver what they promise

This guide will walk you through how to perform Spring Cloud Contract Testing using a sample Spring Boot application in the package com.kscodes.springboot.

Contract Testing with Spring Cloud Contract in Spring Boot

πŸ“¦ What is Spring Cloud Contract?

Spring Cloud Contract is a framework that:

  • Lets you write contracts using Groovy or YAML DSL
  • Generates producer-side tests from the contract
  • Publishes stubs for consumers to use in tests
  • Ensures consumer and provider stay in sync

βš™οΈ Scenario Setup

Let’s assume two services:

  • Producer Service: Exposes /api/users/{id} endpoint
  • Consumer Service: Calls that endpoint and expects a specific response

πŸ“‚ Directory Structure (Producer)


com.kscodes.springboot
β”œβ”€β”€ model
β”‚   └── User.java
β”œβ”€β”€ controller
β”‚   └── UserController.java
β”œβ”€β”€ contract
β”‚   └── user-contract.groovy
└── UserApplication.java

πŸ“˜ Producer Application

User.java


package com.kscodes.springboot.model;

public class User {
    private Long id;
    private String name;

    // Getters and Setters
}

UserController.java


package com.kscodes.springboot.controller;

import com.kscodes.springboot.model.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "John Doe");
    }
}

πŸ“œ Contract File (user-contract.groovy)


package contracts

org.springframework.cloud.contract.spec.Contract.make {
    description "should return user by id"

    request {
        method GET()
        url("/api/users/1")
    }

    response {
        status OK()
        body([
            id: 1,
            name: "John Doe"
        ])
        headers {
            contentType(applicationJson())
        }
    }
}

🧾 Maven Configuration (Producer)

Add the plugin to pom.xml:




    
        
            org.springframework.cloud
            spring-cloud-contract-maven-plugin
            4.1.0
            true
            
                com.kscodes.springboot.BaseContractTest
            
        
    



    
        
            org.springframework.cloud
            spring-cloud-dependencies
            2023.0.1
            pom
            import
        
    


BaseContractTest.java


package com.kscodes.springboot;

import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeEach;
import com.kscodes.springboot.controller.UserController;

public class BaseContractTest {
    @BeforeEach
    public void setup() {
        RestAssuredMockMvc.standaloneSetup(new UserController());
    }
}

πŸ”§ Generate Stubs

Run:


./mvnw clean install

This will generate:

  • target/stubs/
  • UserControllerTest class (generated from contract)

🀝 Consumer Setup

Add dependencies for stub runner:



    org.springframework.cloud
    spring-cloud-starter-contract-stub-runner
    test


Example Test in Consumer Service


package com.kscodes.springboot;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.stubrunner.junit5.StubRunnerExtension;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.junit.jupiter.api.extension.ExtendWith;

import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@ExtendWith({SpringExtension.class, StubRunnerExtension.class})
@SpringBootTest
@org.springframework.cloud.contract.stubrunner.junit5.StubRunner(
        ids = "com.kscodes.springboot:producer:+:stubs:8085",
        stubsMode = StubRunnerProperties.StubsMode.LOCAL
)
public class ConsumerContractTest {

    @Test
    public void testGetUserStub() {
        get("http://localhost:8085/api/users/1")
                .then()
                .statusCode(200)
                .body("name", equalTo("John Doe"));
    }
}

πŸ” Contract Testing Flow


Consumer β†’ defines expectation via contract
          ↓
Contract is shared with Producer
          ↓
Producer runs auto-generated tests
          ↓
Stubs are generated and used in consumer tests

βœ… Benefits of Spring Cloud Contract Testing

BenefitDescription
πŸ§ͺ Confidence in APIsEnsures compatibility between services
🀝 CollaborationContracts serve as clear agreements
🚫 No real backend neededConsumers can test without hitting real provider
🧩 Faster pipelinesIntegration tests become stable and fast

🧠 Best Practices

  • Use versioned contracts stored in a shared repo or artifact store (like Artifactory)
  • Use YAML if you prefer more readable format
  • Validate contracts in CI/CD using stub verification
  • Write negative and edge case contracts (e.g. 404, 500)

πŸ“š Summary

In this post, you explored how to implement Spring Cloud Contract Testing to validate API interactions between microservices. You learned:

  • How to write Groovy contracts
  • How to generate provider-side tests
  • How to use stubs in consumer services
  • Benefits of replacing brittle integration tests

Using Spring Cloud Contract Testing improves confidence, speeds up development, and reduces runtime surprises in distributed systems.