📜
Contract Testing Lab
Practice detecting breaking changes between API v1 and v2
API v2 introduces several breaking changes. Use these to practice contract testing, schema validation, and migration scripts.
Users(4 changes)
Renamed
name → fullNameField renamed from "name" to "fullName"
v1
name: stringv2
fullName: stringAdded
phoneNew required field added
v2
phone: string (required)Added
metadataNew metadata object with version tracking
v2
metadata: { version, createdAt, updatedAt }Structure
response wrapperResponse structure changed
v1
{ data: [...], total }v2
{ users: [...], meta: {...} }Products(5 changes)
Structure
price → pricingSimple number replaced with object
v1
price: 99.99v2
pricing: { amount: 99.99, currency: "USD", formatted: "$99.99" }Structure
inStock → availabilityBoolean replaced with object
v1
inStock: truev2
availability: { status: "in_stock", quantity: 50 }Added
tagsNew array field for product tags
v2
tags: string[]Renamed
query paramsFilter parameter names changed
v1
minPrice, maxPrice, inStockv2
minAmount, maxAmount, availabilityStructure
response wrapperResponse structure changed
v1
{ data: [...], pagination }v2
{ products: [...], meta: {...} }Contract Testing Practice
What to Test
- Field renames (name → fullName)
- New required fields (phone)
- Type changes (price: number → pricing: object)
- Response structure changes (data → users/products)
- Query parameter renames
Tools to Use
- Pact for consumer-driven contracts
- OpenAPI diff tools (oasdiff, optic)
- JSON Schema validators
- Postman contract tests
- RestAssured schema validation
Example Test Cases
// Test: Detect breaking change in user response
test('v2 users response has fullName instead of name', async () => {
const v1 = await fetch('/api/v1/users').then(r => r.json());
const v2 = await fetch('/api/v2/users').then(r => r.json());
// v1 uses "data" wrapper, v2 uses "users"
expect(v1).toHaveProperty('data');
expect(v2).toHaveProperty('users');
// v1 has "name", v2 has "fullName"
expect(v1.data[0]).toHaveProperty('name');
expect(v2.users[0]).toHaveProperty('fullName');
expect(v2.users[0]).not.toHaveProperty('name');
});