What contract testing checks

That the shape both sides expect is the same shape. Field names, types, required and optional, status codes. If a producer removes a field a consumer depends on, contract testing fails at build time instead of at three in the morning.

For a system with many services and independent deployments, this is genuinely valuable and much cheaper than end-to-end integration environments.

What it deliberately does not check

  • Whether the value is right. A correctly shaped response containing the wrong total satisfies every contract.

  • Whether the flow works. Each call can satisfy its contract while the sequence produces a nonsense outcome.

  • What the user sees. Contracts are about services talking to services. Nobody has opened the product.

None of this is a criticism. A contract test that checked values would be coupled to data and would break constantly, which is exactly what contract testing was designed to avoid. The narrowness is the feature.

The failure mode to watch for

A team adopts contract testing, the integration failures stop, and confidence rises. Six months later something breaks in production that every contract test passed, because the shape was right and the behavior was wrong. The lesson people draw is usually that contract testing failed, and that is the wrong lesson.

Contract testing did its job. The gap was that nothing was checking behavior, and the success of the contract layer made that gap harder to see.

What belongs alongside it

Functional verification against the running services: does the sequence of calls produce the right outcome, do error paths behave, does authorization hold. That is a different layer and it answers the questions contracts cannot.

Those checks need to handle sessions, values captured between calls, ordering and cleanup, which are covered in the API testing documentation.

Terminal

npm install -g @testsprite/testsprite-cli
testsprite setup

If you would rather not install anything, the dashboard does the same thing. Everything else the command line can do is in the CLI repository.

Consumer-driven, and why it usually wins

The two approaches differ in who writes the contract, and the difference shows up a year later rather than immediately.

Producer-driven contracts encode what the service intends to offer. That is useful and it tends to be generous: the contract describes the full response, including fields nobody consumes. Breaking changes to unused fields then fail the build, and teams start ignoring contract failures, which defeats the purpose.

Consumer-driven contracts encode what each consumer actually reads. The contract is smaller, the failures are real, and you get a genuinely useful by-product: a precise map of which fields matter to whom. That map is what lets a producer deprecate something confidently, which is the thing everyone actually wanted.

Where each one runs

Contract tests at build time, fast and on every commit, because their whole value is catching shape drift before integration. Behavioral verification against a deployed environment, on every pull request.

The trigger matters more than the mechanism. Pointing it at your deployment event means every change is checked without anyone deciding to; the GitHub App does that from the dashboard, and a GitHub Actions step does it from inside your workflow.

What TestSprite answers that contracts cannot

Whether the values are right and whether the sequence produces the correct outcome. It exercises the running services through their interfaces, so a response that satisfies every contract and contains the wrong total is caught.

Multi-step flows are the specific gap. Integration Tests chain calls where one response feeds the next, and Dependency Chains derive a safe order from what each case needs and produces. Auto-Authentication keeps sessions alive across a run, Dynamic Variables carry values between calls, Dependency Chains derive execution order from what each case needs and produces, and Auto-Cleanup removes exactly what the run created.

What you end up with is two layers that do not overlap: contracts catching shape drift at build time, cheaply and on every commit, and behavioral verification catching the correctness questions contracts are deliberately blind to.

Does TestSprite do contract testing?

No. It verifies behavior against running services. Contract testing is a different instrument and the two complement each other.

Is contract testing enough for microservices?

It is the right answer for shape drift between services and does not tell you whether a multi-service flow produces the correct result.

Consumer-driven or producer-driven?

Consumer-driven catches more real breakage because it encodes what consumers actually use rather than what the producer intended to offer.

Do we still need integration tests?

Fewer of them, which is the point. Keep a small set covering flows that span services, and let contracts handle the shape question.

What if we control both sides?

The value drops, because you can change both together. It is most useful when teams deploy independently.

The short version

Right shape, unknown behavior.

Contract testing catches shape drift between services cheaply and is deliberately blind to whether either side is correct. Keep it at build time, and put behavioral verification against the running services alongside it.