# Install
npm install -g @stoplight/prism-cli
# Start mock server from OpenAPI spec
prism mock ./api/openapi.yaml --port 4010
# Prism validates:# - Request parameters match the spec# - Response body matches the spec schema# - Required headers are present# Proxy to real server and validate responses
prism proxy ./api/openapi.yaml http://localhost:3000 --port 4010
# Every real response is validated against the spec
// Consumer tests run against Prism mock — no real server neededconst client = newOrderClient('http://localhost:4010');
// Prism returns example values from the specconst order = await client.getOrder('123');
// Prism validates the request matches spec and returns spec-compliant response
dredd — Spec Smoke Tests
# Install
npm install -g dredd
# Run spec against real server
dredd ./api/openapi.yaml http://localhost:3000
# dredd calls every endpoint in the spec with example values# and validates actual responses against the spec# Configuration filecat > dredd.yml << EOF
dry-run: false
hookfiles: ./dredd-hooks.js
language: nodejs
sandbox: false
server: npm start
server-wait: 5
endpoint: 'http://localhost:3000'
path:
- ./api/openapi.yaml
reporter:
- dot
- junit
output:
- ./test-results/dredd.xml
EOF
// v1
message Order {
string order_id = 1;
string status = 2;
}
// v2 — SAFE changes:
message Order {
string order_id = 1; // Same field number — compatible
string status = 2; // Same field number — compatible
string customer_id = 3; // New field — old readers ignore it ✅
}
// v2 — BREAKING changes:
// - Renaming field 1 (wire encoding uses numbers, but tooling breaks)
// - Changing field 1 type from string to int
// - Reusing field number 2 for a different field (corrupts old data)
Breaking Change Detection with oasdiff
# Install
brew install tufin/tufin/oasdiff
# or
go install github.com/tufin/oasdiff@latest
# Compare two OpenAPI specs
oasdiff breaking api/v1/openapi.yaml api/v2/openapi.yaml
# Output:# [error] DELETE /api/orders/{id} - deleted endpoint# [error] GET /api/orders: response property 'customerId' removed# [warning] GET /api/orders: new required request header 'X-Request-ID'
# .github/workflows/api-breaking.ymlname:APIBreakingChangeDetectionon:pull_request:paths:-'api/**/*.yaml'jobs:breaking-check:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4with:fetch-depth:0-name:Installoasdiffrun:goinstallgithub.com/tufin/oasdiff@latest-name:Checkforbreakingchangesrun:|
git show HEAD~1:api/openapi.yaml > /tmp/old-spec.yaml
oasdiff breaking /tmp/old-spec.yaml api/openapi.yaml
# Exits non-zero if breaking changes found
Testing Pyramid for Contracts
┌─────────────────┐
│ NEVER │ E2E tests for contract validation
│ (too fragile) │ (service combinations in staging)
└─────────────────┘
┌──────────────────────┐
│ INTEGRATION │ Provider verification tests
│ (real service) │ (verify pact against real impl)
└──────────────────────┘
┌──────────────────────────┐
│ UNIT │ Pact consumer tests
│ (no real service) │ (fast, isolated, in CI)
└──────────────────────────┘
Reference
api-contract — Contract-First API design, OpenAPI spec generation