API Contract Testing: Pact, OpenAPI & Consumer-Driven Guide
Contract testing lets consumer and provider services verify compatibility independently β no shared environment, no end-to-end flakiness. This guide covers consumer-driven contracts with Pact, OpenAPI schema validation with schemathesis, and pipeline integration.
Contract Testing vs Integration Testing
| Criteria | Contract Testing (Pact) | Integration Testing | Schema Testing (OpenAPI) |
|---|---|---|---|
| Environment needed | None (mock provider) | Both services running | Provider only |
| Speed | Fast (unit-level) | Slow | Moderate |
| Who defines spec | Consumer | Shared | Provider (OpenAPI) |
| Catches breaking changes | Yes (per consumer) | Yes | Yes (spec drift) |
| Independent deploys | Enabled | Requires coordination | Partial |
Pact Consumer Test (Node.js / Jest)
The consumer defines the expected interaction. Pact spins up a mock provider, runs the consumer code against it, and saves the resulting contract as a JSON file:
// consumer.pact.test.ts
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
import { getUserById } from '../api/usersClient';
const { like, string, integer } = MatchersV3;
const provider = new PactV3({
consumer: 'orders-service',
provider: 'users-service',
port: 8080,
dir: './pacts', // contract files saved here
});
describe('Users Service contract', () => {
it('returns a user by ID', async () => {
await provider
.given('user 42 exists')
.uponReceiving('a request for user 42')
.withRequest({
method: 'GET',
path: '/users/42',
headers: { Authorization: like('Bearer token') },
})
.willRespondWith({
status: 200,
headers: { 'Content-Type': string('application/json') },
body: {
id: integer(42),
name: string('Vic Chen'),
email: string('[email protected]'),
},
})
.executeTest(async (mockServer) => {
const user = await getUserById(mockServer.url, '42');
expect(user.id).toBe(42);
expect(user.name).toBeDefined();
});
});
});
// After tests: ./pacts/orders-service-users-service.json is created Pact Provider Verification
The provider loads contracts from the Pact Broker and verifies its real implementation satisfies every consumer's expectations:
// provider.pact.test.ts β run in the users-service CI pipeline
import { Verifier } from '@pact-foundation/pact';
import { app } from '../src/app';
import http from 'http';
describe('Pact Provider Verification', () => {
let server: http.Server;
let port: number;
beforeAll(() => new Promise(resolve => {
server = app.listen(0, () => {
port = (server.address() as any).port;
resolve(undefined);
});
}));
afterAll(() => server.close());
it('verifies all consumer contracts', async () => {
await new Verifier({
providerBaseUrl: `http://localhost:${port}`,
pactBrokerUrl: process.env.PACT_BROKER_URL,
pactBrokerToken: process.env.PACT_BROKER_TOKEN,
provider: 'users-service',
publishVerificationResult: true,
providerVersion: process.env.GIT_SHA,
// State handlers β set up test data per consumer scenario
stateHandlers: {
'user 42 exists': async () => {
await db.users.upsert({ id: 42, name: 'Vic Chen', email: '[email protected]' });
},
'no users exist': async () => {
await db.users.deleteAll();
},
},
}).verifyProvider();
});
}); OpenAPI Contract Testing with Schemathesis
Schemathesis generates hundreds of test cases from your OpenAPI spec and runs them against your live API, finding schema violations and crashes automatically:
# Install schemathesis
pip install schemathesis
# Test against a live staging API
schemathesis run https://staging-api.example.com/openapi.json \
--checks all \
--auth "Bearer $API_TOKEN" \
--stateful=links \ # Follow OpenAPI link objects for stateful flows
--max-examples=200 \ # Property-based test cases per endpoint
--junit-xml=results.xml # Output for CI
# Or test a local spec against a local server
schemathesis run ./openapi.yaml --url http://localhost:8080
# Example findings schemathesis catches:
# - 500 errors on valid inputs matching the spec
# - 200 responses with missing required fields
# - Accepting strings where the spec declares integer
# - Missing fields in error responses
# CI step in GitHub Actions
- name: API contract tests
run: |
schemathesis run $STAGING_URL/openapi.json \
--auth "Bearer ${{ secrets.API_TOKEN }}" \
--checks all \
--junit-xml=schemathesis-results.xml
continue-on-error: false Related Products
on GumroadPremium Workflow Pack
Production-grade workflows, architecture guides, and deployment scripts for teams that ship. 10 advanced CI/CD workflows (blue-green, canary, E2E, security), 5 architecture deep-dives, code review checklist, PR process guide, and 5 battle-tested Bash deployment scripts.
Buy on Gumroad βDevPlaybook Interview Vault
210+ real technical interview questions with model answers, asked at Google, Meta, Amazon, Microsoft, Stripe, and Shopify. Covers Frontend (55), Backend (55), System Design (50), and DSA (50). Available in English and Traditional Chinese.
Buy on Gumroad βGitHub Actions CI/CD Templates Pack
12 production-ready GitHub Actions workflow templates covering Node.js, Python, Docker, multi-environment deployments, security scanning, and auto-changelog. Copy, paste, ship.
Buy on Gumroad βYou Might Also Need
OpenAPI Validator
Paste your OpenAPI 3.x spec (YAML or JSON) to validate structure, find missing required fields, detect duplicate paths, check for missing operationIds, and get errors and warnings with path references.
Open free tool β
API Response Mock Generator
Generate realistic mock API responses with custom status codes, headers, and JSON body. Quick templates for 200, 401, 404, 429, 500 and more. Runs in your browser.
Open free tool β
API Response Time Tester
Test API endpoint response times directly from your browser. Run multiple requests, see average/min/max times, status codes, and response sizes. GET, POST, PUT, DELETE support. Free.
Open free tool β
More Free Tools
Related Articles
Microservices vs Monolith in 2026: When to Choose What
The honest guide to choosing between microservices and monolithic architecture in 2026. Real tradeoffs, migration strategies, service mesh, event-driven patterns, and case studies from Netflix, Uber, and Shopify.
Microservices vs Monolith: A 2026 Decision Framework
Stop debating, start deciding. A practical framework for choosing between microservices and monolith architecture in 2026βwith real cost analysis, migration strategies, and a concrete decision flowchart.
Microservices vs Monolith Architecture in 2026: Complete Decision Guide
Should you build a monolith or microservices? A practical 2026 guide covering pros/cons, migration strategies, real-world decision frameworks, and code examples with Docker Compose and Kubernetes.
API Testing Best Practices 2026: Postman vs Insomnia vs Bruno
Complete guide to API testing in 2026. Compare Postman, Insomnia, and Bruno, learn best practices for REST and GraphQL testing, and build automation pipelines that catch regressions before production.
DevOps Automation Tools in 2026: Orchestrating the Software Delivery Pipeline
Master DevOps automation with this guide to the tools that orchestrate modern software delivery. Covers CI/CD pipelines, infrastructure provisioning, configuration management, testing automation, and the emerging AI-assisted automation patterns that define 2026.
Enjoying this tool? Get more like it.
Weekly developer tool guides, productivity playbooks & AI tricks. Free. No spam.
Unlock AI-Powered Dev Tools
- β‘ AI Code Review, Doc Generator & SQL Builder
- β‘ All premium templates & early access
- β‘ Member discounts on Gumroad products
$9/mo after trial Β· Cancel anytime
Want the full toolkit?
Get DevPlaybook Pro
Every template, guide, boilerplate, and automation script in one bundle. 13 premium products β grab them all at once and save big.
MIT licensed Β· Instant download Β· No subscription
See what's included β Browse all deals βFrequently Asked Questions
What is the difference between contract testing and integration testing?▼
Integration tests deploy both the consumer and provider together and test real interactions. They are slow, flaky, and require a shared environment. Contract tests decouple this: the consumer records its expectations (the contract) as a file, and the provider verifies it independently against its actual implementation without needing the consumer running. Contract tests are faster, more targeted, and enable independent deployments because each team can verify compatibility without orchestrating a shared test environment.
What is consumer-driven contract testing?▼
Consumer-driven contract testing means the consumer (the service making API calls) defines the contract β the minimum shape of requests and responses it needs. The provider then verifies that its implementation satisfies every consumer's contract. This approach prevents providers from breaking consumers they don't know about and gives providers a clear spec for what consumers actually use, rather than testing an assumed API surface.
What is Pact Broker?▼
Pact Broker is a central repository for storing, versioning, and sharing Pact contract files between consumer and provider teams. Without a broker, teams pass contract JSON files manually (via git or shared storage). The Pact Broker provides a UI for viewing compatibility matrices, webhooks to trigger provider verification when a new contract is published, 'can I deploy' queries to check if a version is safe to deploy, and network graphs of service dependencies.
What is the difference between Pact and schema-based contract testing?▼
Pact is consumer-driven: consumers generate contracts from real interactions in tests, capturing only what they actually use. Schema-based testing (OpenAPI + schemathesis/dredd) validates that the provider's actual responses conform to its declared OpenAPI specification. These approaches are complementary β schema testing catches spec drift from the provider's side, while Pact catches breaking changes from the consumer's perspective.
What is schemathesis?▼
Schemathesis is an OpenAPI contract testing tool that automatically generates test cases from your OpenAPI spec using property-based testing. It creates hundreds of valid and invalid inputs based on your schema definitions and runs them against your live API, looking for schema violations, 500 errors, crashes, and security issues like input that should be rejected but isn't. It is more thorough than hand-written tests because it explores edge cases you would not think to write manually.
How do I integrate contract testing into CI/CD?▼
For Pact: consumers run tests and publish contracts to Pact Broker on every PR. The Pact Broker triggers provider verification via webhook. Before deploying, both teams run pact-broker can-i-deploy to confirm compatibility. For OpenAPI: run schemathesis against the staging environment as a step in the provider's deployment pipeline. Gate deployments on test results. The goal is making it impossible to deploy a breaking change without knowing about it first.