HomeGuides › API contract testing

API contract testing: stop breaking your consumers

By the Flasqo team · Updated 24 August 2026

In short

Contract tests assert on the shape of a response, not a sample of it — which is exactly why integration tests miss schema drift. This guide covers what counts as a breaking change and how to catch one in CI.

  • Contract testing verifies the whole shape of a response — field names, types, nullability, required properties — rather than the handful of fields a given test happens to read.
  • That is precisely why integration tests miss schema drift: they assert on a sample, so a field that changes type without being read still passes.
  • Removing or renaming a field, narrowing a type, or making a parameter required are breaking changes. Adding an optional field generally is not.
  • Contract failures should block the build, because the alternative is discovering them when a consumer breaks in production.

What is contract testing?

A contract is the agreement between an API and the code that calls it: these fields will be present, they will have these types, this one may be null and this one may not. Contract testing verifies that the running service still honours that agreement — before a change reaches the consumers who depend on it.

It sits between unit testing and integration testing in cost and above both in a specific kind of value: it catches the class of defect where nothing is broken from the provider's point of view, and everything is broken from the consumer's.

Why integration tests miss schema drift

This is the argument for contract testing, and it is worth being precise about.

A typical integration test creates an order and asserts that the response contains an id and a status of "pending". It reads two fields. Now suppose a refactor renames customer_email to customerEmail, or changes total from a number to a string "49.99", or makes shipping_address nullable.

The integration test still passes — it never read those fields. Meanwhile the mobile app that renders the order confirmation crashes on a null address, and the billing service that parses total as a float starts throwing. The provider's test suite is green throughout.

Integration tests assert on a sample of the response. Contract tests assert on its shape. That difference is the entire reason the discipline exists.

What counts as a breaking change

ChangeBreaking?Why
Removing a response fieldYesConsumers reading it get undefined
Renaming a fieldYesEquivalent to removing one and adding another
Narrowing a type (number → integer)YesPreviously valid values now rejected
Making a response field nullableYesConsumers may not handle null
Making a request parameter requiredYesExisting callers omit it
Removing an enum valueYesConsumers may map on it exhaustively
Adding an optional response fieldNoExisting consumers ignore it
Adding an optional request parameterNoDefaults preserve current behaviour
Adding a new endpointNoNothing depends on it yet
Adding an enum valueSometimesBreaks consumers with exhaustive switches

The pattern is that additive change is safe and subtractive change is not. This is why mature APIs accumulate fields rather than reshaping them, and why deprecation is a scheduled process rather than a deletion.

Provider-driven vs consumer-driven contracts

Provider-driven

The provider publishes a schema — typically an OpenAPI document — and tests that its responses validate against it. Simple to set up, and it catches drift between documentation and implementation. Its weakness is that it tells you nothing about which changes actually matter, because it does not know who reads which field.

Consumer-driven

Each consumer publishes the subset of the response it genuinely depends on. The provider verifies it satisfies every published expectation before shipping. This is more work to establish and considerably more useful: it converts "we think this field is unused" into a checkable fact, and makes the blast radius of a change explicit before it ships.

Putting it in the pipeline

  1. Capture the current schema as a baseline. Generate it from live responses if you have no specification — an inferred schema is far better than none.
  2. Validate every response against it on each run. Not just the fields the test reads: the entire body.
  3. Classify differences automatically. Additive changes pass with a note; subtractive changes fail.
  4. Fail the build on breaking changes. Contract violations are one of the few failure classes that should block a merge unconditionally.
  5. Version deliberately when you must break. Introduce the new shape alongside the old, migrate consumers, then remove the old one on an announced schedule.

Contract testing and microservices

The value of contract testing scales with the number of services. In a monolith, a rename is caught by the compiler. Across twelve services deployed independently, nothing catches it — the calling service is compiled, tested and deployed against a version of the interface that no longer exists.

This is also why end-to-end environments are a poor substitute. Standing up every service to test one change is slow, expensive and flaky, and it still only exercises the paths the end-to-end test happens to cover. Contract tests give you most of that assurance without the environment.

Test your API without writing the tests

Paste a URL. Flasqo discovers your endpoints, generates the suite and runs it — free, no credit card.

Start testing free

Frequently asked questions

What is contract testing?

Contract testing verifies that the shape of an API response still matches what its consumers expect — field names, types, nullability and required properties. It catches breaking changes at build time instead of at the moment a client application fails in production.

What counts as a breaking change in an API?

Removing a field, renaming a field, narrowing a type, making an optional request parameter required, or removing a previously valid enum value all break existing consumers. Adding an optional response field or a new endpoint generally does not, which is why additive change is the safe default.

Why do integration tests miss schema drift?

Integration tests usually assert on the handful of fields the test itself uses. If a response gains, loses or retypes a field the test never reads, it still passes — while a consumer that does read that field breaks. Contract tests assert on the whole schema, not a sample of it.

What is consumer-driven contract testing?

Each consumer publishes the subset of the response it actually depends on, and the provider verifies it satisfies every published expectation before shipping. It prevents the provider from guessing which fields are safe to change and makes the blast radius of a change explicit.

Related reading

API Testing: The Complete Guide API Regression Testing: Prevent Breaking Changes in Production API Fuzz Testing: Break It Before Attackers Do API Integration Testing: Validate Multi-Endpoint Workflows API Auto-Discovery: Endpoint Detection & Scoring Vibe Testing: AI-Powered Test Generation From Real Artifacts Visual Flow Builder: Drag-and-Drop API Test Workflows Flasqo vs Apidog 11 Best Free API Testing Tools in 2026 9 Best Postman Alternatives in 2026 Flasqo vs Postman