Skip to content
CodeKruPro

API testing

Contracts, mocks and API styles

Contract tests, mocks and stubs, and REST compared with SOAP and GraphQL.

Learn: Contracts, mocks and API styles: REST, SOAP, GraphQL and contract testing8 minute read

An API contract is everything a client may rely on: paths, methods, request and response shapes, status codes and error formats. Senior API questions move beyond single requests to contracts: how different API styles change testing, how to stop one team's change from breaking another team's client (a program that calls the API), and when to replace a real service with a fake one. This article covers the styles, contracts and fakes (test doubles) you need to discuss.

REST, SOAP and GraphQL

REST is an architectural style on top of HTTP: resources with URLs, standard methods, status codes, and usually JSON. Tests focus on methods, codes, headers and JSON bodies.

SOAP is a protocol, a strict set of rules for the messages. Every message is an XML Envelope (the outer XML tag that wraps the message) with an optional Header and a Body, usually sent with POST to one endpoint. A WSDL file (a description of the service written in XML) lists the operations and message formats, and errors come back as a Fault element. Testing means building XML requests from the WSDL, asserting values with XPath and checking Fault handling. SOAP is still common in banking and older enterprise systems.

GraphQL usually has one endpoint. The client sends a query naming exactly the fields it wants, for example only name and price of a product; requests that change data are called mutations. For testers the differences are: many servers answer 200 and put problems in an errors array, so tests must check it; the typed schema is the contract; authorization must be checked per field; and deeply nested queries can overload a server, so depth limits need tests.

OpenAPI and breaking changes

OpenAPI is a common way to write a REST contract in a file that both people and programs can read. It helps testing in three ways: tests can validate real responses against its schemas, catching drift (the code and the documentation slowly stop matching); a diff tool in CI (a tool that runs automatically on every code change) can compare the new definition with the released one and flag breaking changes (changes that make existing clients fail); and it can generate mocks and request examples.

Breaking changes are changes that make existing clients fail. They include removing or renaming a field, changing its type, making an optional field required, and changing status codes or error formats. Adding an optional field or a new endpoint is normally safe for well-behaved clients. When a breaking change is needed, APIs publish a new version, in the path (/v2/orders), a header or a parameter, and keep testing the old version until it is retired.

Contract testing

An integration test runs real services together. It is realistic, but slow, needs a full environment, and fails for reasons unrelated to your change. A contract test checks only the agreement between two parties: a consumer, the program that calls an API, and the provider, the service that answers. In consumer-driven contract testing, with a tool such as Pact:

  1. The consumer, for example the web app, runs its tests against a mock of the provider. The interactions it relies on are recorded as the contract.
  2. The contract is published, often to a broker (a shared server that stores contracts).
  3. The provider's pipeline (its automatic build and test steps) replays each contract against the real provider and fails if a response no longer matches.
The consumer's tests record a contract against a mock provider; the contract goes to a broker; the provider pipeline fetches it, replays the requests against the real provider API and fails on a mismatch.
Consumer-driven contracts: each side tests on its own, against the shared contract.

The provider learns before deploying that renaming total would break the mobile app, and also which fields nobody uses. Contract tests check the shape of the conversation, not business flows across services, so a few end-to-end tests are still needed.

Stubs, mocks and service virtualization

Replace a dependency when the real one is unavailable, slow, costly, unstable or impossible to put into the state you need: a payment provider, an SMS gateway, a partner API, or a service not built yet.

  • A stub returns fixed, ready-made answers so the test can run, and checks nothing about how it was called.
  • A mock also verifies the interaction: was it called, how often, with which data? "Exactly one refund request for 499" is a mock check.
  • Service virtualization simulates a whole service with many endpoints, state, delays and failures, often recorded from real traffic and shared across teams. Tools such as WireMock cover these over HTTP.

Fakes let you test what real systems rarely produce on demand: timeouts, 500s, slow or malformed answers. Their risk is drift, where the fake keeps saying yes after the real service changed. Contract tests, or a few tests against the real dependency, keep the fake honest.

How to answer in an interview

For "REST versus SOAP", lead with style versus protocol, then one testing difference each. For "What is contract testing?", describe the three steps and give the renamed-field example. For microservices (an application built from many small services), name the layers: API tests per service with dependencies faked, contract tests between services, a few end-to-end tests that follow a real user journey through all services, and polling with a time limit (asking again and again until the result appears or time runs out) for asynchronous messages, which are processed later in the background.

Common mistakes

  • Saying REST must use JSON, or that SOAP is "just XML over REST".
  • Trusting a GraphQL 200 without checking the errors array.
  • Calling every fake a mock; a mock verifies calls, a stub only answers.
  • Thinking contract tests replace all integration tests.

Recap

REST is a style, SOAP a strict XML protocol, and GraphQL one endpoint with client-chosen fields. OpenAPI and versioning manage change; consumer-driven contracts catch breaking changes before deploy without running both services together. Stubs answer, mocks also verify, and service virtualization simulates whole services; keep every fake in line with the real thing.

11 questions0/11 solved

Not startedNot startedIn progressIn progressSolvedSolvedPro+Pro+

The first 2 questions of each topic are free. Pro+ opens every question.

Back to the plan