Menu

Topic 1 of 8

Contract Testing Basics

Learn Contract Testing Basics for free with explanations, exercises, and a quick test (for API Engineer).

Published: January 21, 2026 | Updated: January 21, 2026

Why this matters

As an API Engineer, your services evolve independently from their consumers. Contract testing lets teams deploy safely without coordinating every release. It catches breaking changes early, before integration or production. Typical real tasks:

  • Validate that your API still returns fields mobile apps rely on.
  • Ensure status codes and headers stay consistent during refactors.
  • Prove a provider change is backward compatible for existing consumers.
  • Safely integrate third-party services via agreed request/response shapes.

Concept explained simply

Think of a contract like a restaurant menu between a diner (consumer) and the kitchen (provider). The menu states what can be ordered and what will be served. If the kitchen changes a dish without updating the menu, the diner is surprised. Contract tests are automated checks that the menu and the kitchen match.

Mental model

  • Consumer writes expectations: "When I call GET /users/42, I expect 200 and these fields."
  • Provider verifies: "My service actually returns that for a prepared state."
  • If both sides pass, deployments can proceed independently.

Key terms (tool-agnostic)

  • Consumer: The client of an API (web app, mobile app, another service).
  • Provider: The API service producing responses.
  • Contract: A machine-readable description of request + expected response.
  • Interaction: One request/response pair covered by the contract.
  • Provider state: Data setup to make the provider return a predictable response.
  • Verification: Provider runs tests against contracts to confirm compliance.
  • Stubs: Local mock endpoints generated from the contract for consumer tests.
  • Match rules: Flexible matching (types, patterns, optional fields) to avoid brittle tests.

Typical workflow

  1. Design expectations (consumer): Write interactions for the endpoints you call.
  2. Publish/share contract: Make it available to the provider (e.g., as a file or via a broker).
  3. Verify (provider): Run verification against your running API with realistic provider states.
  4. Automate: Add verification to CI to fail builds on breaking changes.
  5. Evolve: When changing the API, add new interactions and keep old ones passing until consumers migrate.

Worked examples (3)

Example 1 — Happy path: GET /users/42 returns 200

Consumer expectation:

{
  "interaction": "GET /users/42",
  "request": {
    "method": "GET",
    "path": "/users/42"
  },
  "response": {
    "status": 200,
    "headers": {"Content-Type": "application/json"},
    "body": {
      "id": 42,
      "email": "user42@example.com",
      "name": "Alice",
      "createdAt": "2024-05-01T10:20:30Z"
    },
    "matchers": {
      "body": {
        "id": {"type": "number"},
        "email": {"type": "string", "pattern": ".+@.+"},
        "name": {"type": "string"},
        "createdAt": {"type": "string", "format": "iso-datetime"}
      }
    }
  }
}

Provider verification steps:

  • Seed database with user id 42.
  • Start API locally.
  • Run contract verifier; ensure content type and body shape match.
Example 2 — Error path: GET /users/999 returns 404

Consumer expectation:

{
  "interaction": "GET /users/999",
  "request": {"method": "GET", "path": "/users/999"},
  "response": {
    "status": 404,
    "headers": {"Content-Type": "application/json"},
    "body": {"error": "USER_NOT_FOUND", "message": "No user"},
    "matchers": {"body": {"error": {"type": "string"}, "message": {"type": "string"}}}
  }
}

Why it matters: It locks the error contract so clients can handle it reliably.

Example 3 — Create resource: POST /orders

Consumer expectation:

{
  "interaction": "POST /orders",
  "request": {
    "method": "POST",
    "path": "/orders",
    "headers": {"Content-Type": "application/json"},
    "body": {
      "items": [{"sku": "ABC-123", "qty": 2}],
      "customerId": 42,
      "notes": "optional"
    }
  },
  "response": {
    "status": 201,
    "headers": {"Location": "/orders/1001", "Content-Type": "application/json"},
    "body": {"id": 1001, "status": "CREATED"},
    "matchers": {
      "body": {"id": {"type": "number"}, "status": {"enum": ["CREATED", "PAID", "CANCELLED"]}},
      "headers": {"Location": {"type": "string", "pattern": "^/orders/\\d+$"}}
    }
  }
}

Provider verification: Ensure 201 status, Location header format, and body schema match. Notes field is optional, so omit strict matching for it.

Rules of thumb

  • Contracts describe observable behavior, not implementation details.
  • Prefer type/pattern matchers over exact values to reduce flakiness.
  • Include error cases and headers (content type, caching) if consumers rely on them.
  • Use provider states to make responses deterministic.
  • Version carefully: add fields as optional first; remove only after consumers drop old expectations.

Exercises

Complete the exercise below. A matching solution is available in the toggle.

  • Exercise 1: Design a consumer contract for GET /products/{id} and plan provider verification. See details in the Exercises panel on this page.

Exercise checklist

  • Interaction covers request method, path, and any query params.
  • Response includes status, headers, and typed body matchers.
  • Error path included (e.g., 404).
  • Provider states are listed and feasible to set up.
  • No brittle hardcoded values unless absolutely required.

Common mistakes and self-check

  • Mistake: Exact-value matching for dynamic fields (ids, timestamps). Self-check: Are you using type/pattern matchers?
  • Mistake: Ignoring error cases. Self-check: Do you have at least one 4xx or 5xx interaction?
  • Mistake: Missing headers. Self-check: Did you assert Content-Type and any required auth/caching headers?
  • Mistake: Overconstraining optional fields. Self-check: Are optional fields marked as optional or omitted?
  • Mistake: Skipping provider states. Self-check: Can you deterministically reproduce each interaction locally/CI?

Practical projects

  • Two-service demo: Build a small consumer (fetches user) and provider. Write 3 interactions (200, 404, 500). Automate verification in your CI.
  • Backward-compat change: Add a new optional field to the response. Show old contracts still pass while new ones verify.
  • Stub-first development: Generate a stub from the contract and point the consumer at it. Develop UI logic before provider is ready.

Learning path

  1. Master basics (this page): interactions, matchers, provider states.
  2. Add negative and edge cases (rate limits, validation errors).
  3. Introduce versioning strategy and deprecation timelines.
  4. Automate in CI: verification on PR, gating merges on failures.
  5. Expand to message contracts if using events/queues.

Who this is for

  • API engineers and backend developers owning HTTP/JSON services.
  • Integration engineers coordinating multiple microservices.
  • QA engineers building reliable service-level test suites.

Prerequisites

  • Comfortable with HTTP methods, status codes, and JSON.
  • Basic understanding of unit vs integration tests.
  • Ability to run a local API service and seed test data.

Next steps

  • Add contracts for all critical consumer flows.
  • Integrate verification into your CI and block breaking changes.
  • Document provider states so teammates can reproduce results.

FAQ

Contract vs schema validation?

Schemas describe shapes. Contracts pair shapes with real requests, responses, and states, verifying actual behavior end-to-end at the API boundary.

Contract testing vs end-to-end testing?

Contract tests are faster and isolate consumer-provider boundaries. E2E tests cover full systems but are slower and more brittle. Use both, but prefer contracts for daily safety.

Mini challenge

Pick one endpoint you own. Write two interactions: a happy path and an error path. Add one header assertion and one type matcher per field. Timebox: 20 minutes.

Quick Test access

The quick test below is available to everyone. If you log in, your progress will be saved automatically.

Practice Exercises

1 exercises to complete

Instructions

You are building a web storefront that reads product info from a Product API. Define a consumer contract for a single product lookup and outline how the provider will verify it.

  1. Write a 200 OK interaction for GET /products/123 including headers and body with fields: id (number), name (string), price (number), currency ("USD" or "EUR"), inStock (boolean), tags (array of strings, optional).
  2. Write a 404 interaction for GET /products/999.
  3. Add matchers to avoid brittle values (type/pattern/enum where appropriate).
  4. List provider states needed for each interaction.
  5. Describe provider verification steps (startup, seeding, run verifier).
Expected Output
A machine-readable contract with two interactions (200 and 404), typed matchers, required headers, provider states, and a clear verification plan.

Contract Testing Basics — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Contract Testing Basics?

AI Assistant

Ask questions about this tool