DEV Community

levi
levi

Posted on

Bulletproof Your APIs with Contract Testing

πŸ” Real-World Wake-Up Call
Once upon a time at work 😜, I was working on a project that heavily relied on multiple upstream services owned by different teams to process order data. One seemingly ordinary day, one of those services made a change to their API β€” they updated the productDescription field from a String to a List.

Our service, still expecting a String, started throwing 400 Bad Request errors out of nowhere. This seemingly small change caused unexpected failures and impacted our downstream processing.

This incident made it painfully clear: we needed a better way to catch breaking changes in API contracts early, before they hit production. That’s when we started exploring Contract Testing as a solution. Among the tools we considered were Spring Cloud Contract and Pact.
Here url for POC we created: https://github.com/levi-a07/contract-testing

Spring Cloud Contract is a testing framework that helps ensure reliable communication between microservices by using contract testing
A contract is a written agreement (in code) that defines:

  • What the producer (API provider) promises to return, based on what the consumer (API client) sends.

Spring Cloud Contract allows you to:

  • Write these contracts (in Groovy/YAML),
  • Automatically generate tests for the producer,
  • Automatically generate mock stubs for the consumer.

Sample looks like:

description: Should return order by id
request:
  method: GET
  url: /orders/1
response:
  status: 200
  headers:
    Content-Type: application/json
  body:
    orderId: "1"
    productId: "product-1234"
    quantity: 2
    price: 29.99
  matchers:
    body:
      - path: $.orderId
        type: by_regex
        value: "\\d+"   # Matches any numeric orderId
      - path: $.productId
        type: by_regex
        value: "product-\\d+"  # Matches a product ID like product-123
      - path: $.quantity
        type: by_type
        value: number       # Matches any numeric quantity
      - path: $.price
        type: by_type
        value: number       # Matches any numeric price
Enter fullscreen mode Exit fullscreen mode

Types of Contract Testing

There are 3 type of contracts:
1. Consumer-Driven Contracts (CDC)
The contract is usually written from the consumer’s point of view, describing what it expects from the producer. The consumer service then uses a stub β€” an auto-generated mock server based on the contract β€” to simulate the producer’s behavior during testing.

This stub reflects the contract exactly, not the actual producer code. The consumer team can share the contract via a pull request to the producer repo or through a shared location. If the producer later changes the contract, the stub updates accordingly, and any mismatch will cause the consumer's test to fail, alerting the team to the breaking change early.

Based on the contract, Automatic Junit tests are created on Producer side.
The generated tests simulate requests described in the contract and verify that the producer's real implementation returns the expected response. This way we catch breaking changes early.

*Best for *

  • Systems with multiple consumers and independent teams.
  • Ensuring backward compatibility.

Tool

  • Spring cloud contract
  • Pact

2. Producer-Driven Contracts **
The producer defines the contract β€” the API specification β€” and shares it with consumers.
**How it works:

Producer publishes the OpenAPI/Swagger schema or a written contract.
Consumers build against this contract.

Best for

  • Where contracts are mostly controlled by producer service.

Tools
OpenAPI/Swagger

3. Bidirectional Contracts

A combination of consumer and producer contracts β€” both sides define expectations, and they are reconciled in a central place.

Best for:

  • Where contracts evolve from both sides.

Tools
PactFlow

Conclusion:
In a microservices architecture, communication contracts between services are critical β€” yet easy to break. By automating contract validation, generating stubs, and catching breaking changes early in the pipeline, contract testing boosts confidence, improves collaboration across teams, and helps deliver more reliable systems.
In the market we have different tools to use contracts based on your use case.

Top comments (0)