API Contract Testing Explained

Testing · Updated November 2026 · ← All tips

A service passes every integration test, ships, and the next morning a mobile client crashes because a field quietly changed from a number to a string. Nobody broke a test — because no test checked the shape the client actually depends on. That's the gap contract testing closes: it verifies the interface between a provider and its consumers, independently of whether the full system works end to end.

What a contract is

A contract is a precise, machine-checkable description of a request/response pair: which fields exist, their types, which are required, and sometimes allowed values or formats. It is narrower than "does the feature work" and broader than "does this one example match" — a contract test fails when the shape changes, not when a value a client doesn't care about changes.

Contracts usually come from one of two places:

Contract testing vs. integration testing

Integration / E2E testContract test
Needs a running provider and its dependenciesRuns against a schema, a recorded sample, or a mock built from the contract
Verifies business behavior ("the order total is correct")Verifies interface shape ("the response has a numeric total")
Slow, flaky when run across teamsFast, runs in isolation per side
Catches logic bugsCatches breaking changes before they reach consumers

They're complementary, not competing: contract tests catch "this will break someone downstream" early and cheaply; integration tests still confirm the business logic is correct. See our broader REST API testing checklist for where each type fits.

A practical workflow: schema-first contracts

Most teams don't need a dedicated contract-testing framework to get real value — a JSON Schema per endpoint, checked on every response, already catches the majority of breaking changes.

  1. Derive the schema. Generate it from your OpenAPI spec, or write it by hand for endpoints without one yet. Keep it in the repo next to the code that implements the endpoint.
  2. Validate every response against it in your test requests. In RestRuno, a post-response script can run a schema check directly:
const schema = {
  type: 'object',
  required: ['id', 'email', 'createdAt'],
  properties: {
    id: { type: 'number' },
    email: { type: 'string', format: 'email' },
    createdAt: { type: 'string', format: 'date-time' },
    plan: { type: 'string', enum: ['free', 'pro', 'enterprise'] }
  }
};

rr.test('response matches the user contract', () =>
  rr.expect(rr.response.body).toMatchSchema(schema)
);
  1. Run it on every pull request, not just before a release — a contract break caught in CI costs minutes; the same break caught by a consumer in production costs hours. See our guide on automating API tests in CI/CD.
  2. Version the contract explicitly when a change is genuinely breaking, instead of mutating it silently — see API versioning as the contract's release process.

Consumer-driven contracts, when schema-first isn't enough

A shared schema assumes every consumer needs every field it describes. In practice, a mobile app might only read three fields of a twenty-field response, while an internal dashboard reads all twenty. Consumer-driven contract testing flips the direction: each consumer publishes the exact subset of fields and behaviors it relies on, and the provider's CI pipeline replays those expectations against the real implementation before merging. This catches the realistic failure mode — "the field nobody on the provider team thought still mattered" — without forcing every consumer to agree on one giant shared schema.

This pattern pays off once you have more than two or three independent consumer teams; below that, a well-maintained OpenAPI spec plus schema validation usually covers the same ground with far less process.

Where mocks fit in

A contract is also the cleanest input for a mock server: generate one directly from the OpenAPI spec or JSON Schema, and consumer teams can build against a provider that doesn't exist yet, or one that's down. See mocking and stubbing APIs for reliable tests for how to keep that mock honest as the real contract evolves.

Common pitfalls

Quick checklist

Validate contracts without extra infrastructure

RestRuno runs JSON Schema assertions as part of normal post-response tests, in a free local-first client — no server to deploy, no account to create.

Download RestRuno — free