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
.

π¦ 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)
1 2 3 4 5 6 7 8 9 10 11 |
com.kscodes.springboot βββ model β βββ User.java βββ controller β βββ UserController.java βββ contract β βββ user-contract.groovy βββ UserApplication.java |
π Producer Application
User.java
1 2 3 4 5 6 7 8 9 10 11 |
package com.kscodes.springboot.model; public class User { private Long id; private String name; // Getters and Setters } |
UserController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<build> <plugins> <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>4.1.0</version> <extensions>true</extensions> <configuration> <baseClassForTests>com.kscodes.springboot.BaseContractTest</baseClassForTests> </configuration> </plugin> </plugins> </build> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> |
BaseContractTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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:
1 2 3 4 |
./mvnw clean install |
This will generate:
target/stubs/
UserControllerTest
class (generated from contract)
π€ Consumer Setup
Add dependencies for stub runner:
1 2 3 4 5 6 7 8 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-contract-stub-runner</artifactId> <scope>test</scope> </dependency> |
Example Test in Consumer Service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
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
1 2 3 4 5 6 7 8 9 10 |
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
Benefit | Description |
---|---|
π§ͺ Confidence in APIs | Ensures compatibility between services |
π€ Collaboration | Contracts serve as clear agreements |
π« No real backend needed | Consumers can test without hitting real provider |
π§© Faster pipelines | Integration 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.