ToolSura Blog
ArticlesAboutContact
Search

Stay in the loop

Join thousands of developers getting weekly insights into modern web development, AI tools, and productivity.

© 2026 ToolSura Blog
AboutContactPrivacy PolicyTerms of ServiceRSS

    Table of Contents

    What is contract testing?Contract testing vs integration testingHow consumer-driven contracts workPact vs OpenAPI schema validationWhich should you pick?Contract testing in CI/CDDo you still need integration tests?What contract testing does NOT coverBuilding contracts from OpenAPI schemasCommon mistakesRelated readingReferences
    HomeToolsura BlogArticle

    API Contract Testing: Tools, Examples & CI Setup

    A

    Abhay khant

    Jan 1, 1970 • 11 min read

    Your frontend team ships a breaking change to the user payload. Your mobile team still expects the old shape. Neither realizes until production shows a blank screen. API contract testing catches that mismatch before deploy, by checking both sides of an API against a shared agreement instead of hoping they stay in sync.

    Contract testing treats the API as a two-party agreement: the provider (the service exposing the API) and the consumer (the app calling it). Each side tests against the same contract file, so a change on either side fails fast instead of failing in production. It is not a replacement for integration tests — it is a faster, narrower check that runs on every pull request.

    This guide covers how consumer-driven contracts work, when to use Pact versus OpenAPI schema validation, how to wire contract tests into CI/CD, and what contract testing deliberately does not cover.

    What is contract testing?

    A contract is a machine-readable description of an API interaction: the request shape a consumer sends, the response shape the provider returns, and any headers or status codes in between. Both teams test against that description independently, so each side can catch a mismatch without deploying the other.

    Contract testing sits between unit tests and full integration tests. Unit tests check one component in isolation. Integration tests spin up real services and check they talk to each other. Contract tests check that each side honors the shared agreement, using mocks instead of live dependencies. That makes them fast, deterministic, and runnable on every pull request — the three things that let a contract test actually live in your pipeline.

    Contract testing vs integration testing

    The question teams ask most is how contract testing differs from integration testing. The short answer: integration tests verify that two running services work together; contract tests verify that each side would work with the other, before either one is deployed.

    Contract testingIntegration testing
    What it checksEach side honors the shared contractReal services interoperate
    DependenciesMocks/stubs of the other sideLive instances
    SpeedMillisecondsSeconds to minutes
    Where it runsEvery pull requestStaging, pre-release
    Failure tells youWhich side broke the agreementSomething broke, dig deeper

    The practical rule: run contract tests continuously, run integration tests before release. A contract test failing on a PR points at the exact breaking field. An integration test failing in staging tells you the systems don't work together — then you still have to find out why.

    How consumer-driven contracts work

    Consumer-driven contract testing inverts the usual flow. Instead of the provider publishing a spec and consumers hoping it stays accurate, each consumer captures its actual expectations — the requests it really sends and the responses it really handles — into contract files. The provider then verifies every consumer contract against its implementation.

    The flow looks like this:

    1. The consumer records the interactions it depends on (usually from its own test suite).
    2. Those interactions become a contract file, committed alongside the consumer's code.
    3. The consumer runs its side of the test against the contract locally.
    4. The contract is published to a broker or registry.
    5. The provider pulls the contract, runs its own tests against it, and either passes or finds drift.
    6. On a breaking change, the provider sees which consumers would be affected before merging.

    This catches drift at the source. When a consumer's expectations and a provider's implementation disagree, the provider's verification step fails — while the change is still in development, not after deploy. Martin Fowler's consumer-driven contracts article is the canonical explanation of this pattern.

    The trade-off is real: consumer-driven testing shines when you control both sides. It does less for public APIs with unknown consumers, where the contract is your OpenAPI spec and the verification is schema validation against it.

    Pact vs OpenAPI schema validation

    Teams usually end up choosing between two approaches: Pact for consumer-driven contract testing between services you control, and OpenAPI schema validation when the API itself — usually the spec — is the source of truth.

    Pact (and its documentation) records interactions from the consumer's test suite into Pact files, then has the provider verify them. It excels at finding drift on the exact interactions consumers depend on, and its broker gives you a published history of which consumer versions a provider change would break. The cost: it is a workflow, not a library. Teams adopt Pact conventions, brokers, and CI stages.

    OpenAPI schema validation works the other way: the spec is the contract, and both sides validate against it. Tools like Schemathesis generate property-based tests from the schema, sending thousands of automatically-derived requests to find cases the spec didn't anticipate. Dredd compiles the API description into executable tests directly. This approach is simpler to adopt — if you already have an OpenAPI spec, you are most of the way there — but it verifies conformance to the spec, not to what consumers actually do. A consumer that relies on an undocumented field will pass schema validation and break in production.

    Which should you pick?

    • You control both sides, many consumers, breaking changes are frequent → Pact. The broker tells you exactly which consumers break.
    • Public or third-party API, spec is the source of truth → OpenAPI schema validation with Schemathesis or Dredd.
    • Polyglot microservices on the JVM → Spring Cloud Contract generates provider stubs and consumer tests from a shared contract.
    • Small team, existing spec, want the fastest win → Start with schema validation; add Pact-style consumer contracts where drift actually hurts.

    The two approaches are not mutually exclusive. Many teams run Schemathesis against their OpenAPI spec continuously and Pact contracts for the handful of consumers that matter most.

    Contract testing in CI/CD

    Contract tests earn their keep only when they run automatically — and automation is the norm: 75% of API teams run CI/CD pipelines, per Postman's State of the API 2025 report. The pattern: consumers run their side on every push, the provider verifies all consumer contracts on every push, and the broker or registry gates merges when verification fails.

    A minimal Pact CI setup on the consumer side, using the Pact CLI to publish verified contracts:

    name: Consumer Contract Tests
    on: [pull_request]
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: npm ci
          - run: npm test  # runs consumer tests that record Pact interactions
          - run: npx pact-broker publish ./pacts \
              --broker-base-url ${{ secrets.PACT_BROKER_URL }} \
              --broker-token ${{ secrets.PACT_BROKER_TOKEN }} \
              --consumer-app-version ${{ github.sha }}
    

    On the provider side, the verification step pulls every consumer contract and fails the build if any of them no longer matches:

    name: Provider Verify Contracts
    on: [pull_request]
    jobs:
      verify:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: npm ci
          - run: npm run start:provider & sleep 5
          - run: npx pact verify \
              --pact-broker-base-url ${{ secrets.PACT_BROKER_URL }} \
              --pact-broker-token ${{ secrets.PACT_BROKER_TOKEN }} \
              --provider-app-version ${{ github.sha }}
    

    For schema-driven validation, the gate is simpler. Schemathesis runs its generated test suite against the running API on every push:

    name: Schema Validation
    on: [pull_request]
    jobs:
      schemathesis:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: npm ci
          - run: npm run start:api & sleep 5
          - run: schemathesis run http://localhost:3000/openapi.json
    

    The same commands slot into GitLab CI or any other runner — the pattern is what matters: both sides test against the shared agreement, on every change, before merge. Test your OpenAPI spec and a sample payload in the ToolSura API tester or validate the schema itself with the ToolSura JSON validator before wiring it into a pipeline.

    Do you still need integration tests?

    Yes. Contract tests cover the shape of interactions, not their behavior. They will not catch a service that responds correctly but writes the wrong row to the database, a timeout under load, or an auth flow that only fails with real credentials. Keep integration tests at the release boundary — contract tests reduce how often you need them, they do not replace them. For a deeper look at where schema checks fit in a broader testing strategy, see our JSON Schema generator guide.

    What contract testing does NOT cover

    Being explicit about the boundaries saves you from a false sense of safety. Contract testing does not verify:

    • Behavior and side effects. A response that matches the contract can still do the wrong thing. Contract tests check shape, not correctness.
    • Performance and load. A contract test proves the interaction fits the agreement, not that it meets a latency budget.
    • Auth and authorization. Security rules rarely show up in a contract file, and mocking them hides exactly the failures you want to see.
    • Stateful or sequential edge cases. Most contract tools assume one-shot request/response interactions. Multi-step flows, retries, and idempotency are hard to express.
    • Cross-service behavior. Contract tests never deploy both sides. Anything that only fails when two real services interact needs an integration test.
    • Message queues and events. Classic contract testing is HTTP-centric. For asynchronous events, look at Pact's message support (Pact files can describe messages, not just HTTP) or schema registries that validate event payloads — but know that the guarantees are weaker than for request/response.

    Building contracts from OpenAPI schemas

    When the spec is your source of truth, you generate contracts from it instead of recording them. OpenAPI 3.0 defines components that can be lifted into standalone contract files, and validation tools consume them directly. Keep the OpenAPI spec canonical and derive what you need from it, rather than maintaining a second copy by hand — that is how contract drift sneaks back in.

    A minimal contract extracted from an OpenAPI component might look like — note the $schema key pointing at the JSON Schema specification:

    {
      "$schema": "https://json-schema.org/draft/2020-12/schema",
      "title": "CreateUserRequest",
      "type": "object",
      "required": ["email", "name"],
      "properties": {
        "email": { "type": "string", "format": "email" },
        "name": { "type": "string", "minLength": 1, "maxLength": 100 },
        "roles": {
          "type": "array",
          "items": { "enum": ["admin", "user", "viewer"] }
        }
      }
    }
    

    Validate the schema and test payloads against it with the ToolSura JSON validator before wiring it into CI. If you are generating schemas from TypeScript types, our Zod to JSON Schema guide shows how to keep the contract in lockstep with the code that implements it.

    Common mistakes

    • Testing only the happy path. Contracts that omit error responses and status codes let the 400s and 500s drift silently. Include them.
    • Letting the provider own the contract. When the provider writes the contract, it describes what it happens to return — not what consumers depend on. Consumer-driven means consumer-recorded.
    • Maintaining a second contract by hand. Generate contracts from your OpenAPI spec or your types. A hand-edited copy is drift waiting to happen.
    • Treating contract tests as a compliance checkbox. A contract test that never runs in CI is documentation, not protection. It must gate merges to be useful.
    • Ignoring the broker's can-i-deploy. Pact's broker answers "can I safely deploy this provider version?" — teams that skip it lose the whole point of recording consumer versions.

    Related reading

    • JSON Schema generator guide — where schemas come from, and the CI gate pattern
    • Zod to JSON Schema — keeping contracts in lockstep with TypeScript code
    • Quicktype JSON schema inference — deriving schemas from sample payloads
    • TypeScript type safety guide — the compile-time side of the contract
    • Test payloads against live endpoints with the ToolSura API tester
    • Validate schemas and payloads with the ToolSura JSON validator

    References

    • Pact consumer-driven contract testing docs — https://docs.pact.io/
    • Martin Fowler, Consumer Driven Contracts — https://martinfowler.com/articles/consumerDrivenContracts.html
    • Schemathesis — property-based testing for your API — https://schemathesis.readthedocs.io/
    • Dredd — HTTP API testing framework — https://dredd.org/
    • Spring Cloud Contract reference — https://docs.spring.io/spring-cloud-contract/docs/current/reference/html/
    • Postman State of the API 2025 — https://www.postman.com/state-of-api/
    • JSON Schema specification — https://json-schema.org/
    • OpenAPI 3.0 specification — https://spec.openapis.org/oas/v3.0.3.html

    Frequently Asked Questions

    developer-tools
    best-practices
    web-development
    documentation
    open-source
    A

    About Abhay khant

    A passionate tech enthusiast and professional developer specializing in AI, automation, and modern web development. Sharing insights and guides to help others build better software faster.

    View full profile →

    Join the Newsletter

    Get articles like this delivered to your inbox every Thursday.

    What to read next

    Technology Fingerprinting Explained for Developers
    Jan 1, 19705 min read

    Technology Fingerprinting Explained for Developers

    Learn what technology fingerprinting is, how websites reveal their stack, and how developers use Wappalyzergo to detect frameworks and infrastructure.

    AAbhay khant
    Stop Windows from Installing Apps Without Permission
    Jan 1, 197010 min read

    Stop Windows from Installing Apps Without Permission

    LG and Dell monitors silently push apps via Windows Update. Learn how to stop Windows from installing apps without permission and detect what's on your PC.

    AAbhay khant
    Private AI Coding Tools to Keep Your Code Off the Cloud
    Jan 1, 197010 min read

    Private AI Coding Tools to Keep Your Code Off the Cloud

    Run AI coding assistants that never send your source code to the cloud. Compare 6 private, local-first, and self-hosted coding tools for 2026.

    AAbhay khant