Skip to content

Chapter 6 of 7

Contract Testing, Mocks, and Test Patterns

In microservice systems, contract testing verifies that a provider service's API still matches the expectations of its consumers—request and response shapes, status codes, and headers. Pact is the most popular consumer-driven contract testing tool: consumers record their expectations as pacts, providers verify against those pacts in CI, and teams can deploy independently with confidence. In Pact terms, the consumer is the service that calls another API and defines the contract, while the provider is the service that exposes the API and must satisfy all consumer pacts. Consumer-driven contract testing yields particular value when many teams consume the same API; it surfaces contract drift early, before mismatched field names or new required parameters reach production.

Test doubles support both contract and integration work. A mock server simulates a real API's responses, allowing client teams to develop against a stable, controllable fake without waiting on the backend. Subtle distinctions exist between mocks and stubs: a stub returns canned responses to specific calls, while a mock also verifies that expectations were met (for example "this method was called once with these args"); in API testing the terms are often used interchangeably. WireMock is a popular open-source library for stubbing and mocking HTTP services with recorded and playback scenarios, and virtualization tools such as Hoverfly and Mountebank capture real HTTP traffic and replay it as a virtual service, letting tests run against a likeness of upstream APIs without hitting the real dependency. Service virtualization more broadly simulates components that are unavailable, slow, or shared—databases, third-party APIs, mainframes—so dependent services can be tested in isolation. Postman Mock Server serves a mock implementation from a saved collection, parallelizing client and server development. Schema-first development takes this discipline further: writing the OpenAPI specification first, then generating server stubs, client SDKs, and tests keeps implementation, docs, and contracts in sync. OpenAPI describes an API as a whole—paths, operations, parameters, security—while JSON Schema describes the shape of a single JSON document; OpenAPI reuses JSON Schema for its request and response bodies.

Test design choices determine a suite's long-term value. The Arrange-Act-Assert pattern structures each test as setup, action, and verification. Given-When-Then (Gherkin) provides a BDD-flavored, behavior-focused equivalent that reads well to non-engineers. JSONPath lets tests assert on deeply nested fields, and libraries like JSONassert and json-schema-validator perform full or partial shape matching. Typed API client libraries, often generated from OpenAPI, make tests more readable and refactor-safe compared to hand-built payloads. Coverage comes in two flavors: code coverage measures executed lines and branches in the implementation, while API coverage measures which endpoints, status codes, and parameter combinations have been exercised at the HTTP boundary. The test pyramid suggests many unit tests at the bottom, a moderate number of integration and API tests in the middle, and a small number of end-to-end tests at the top; the test trophy variation emphasizes static typing and integration tests because they catch more real-world bugs with less maintenance. Shift-left testing runs API tests as early as possible—in CI on every commit, in pre-commit hooks, or locally—catching defects long before production. A CI pipeline check executes the API suite on every commit and pull request, gating merges on green. Integration tests call the API and may exercise the database or one downstream service, while end-to-end tests exercise the full stack including the UI, real third-party APIs, and live infrastructure. The boundary between unit and component API tests is worth defining: unit tests exercise a single function or class, while component tests drive one service end-to-end through its real HTTP surface—routing, validation, persistence—but still stub external services.

Practical infrastructure supports all of the above. Sandbox environments mirror production closely enough to safely run integration and E2E tests without affecting real users or data. Data masking replaces sensitive PII or payment fields with realistic-looking but fake values, and test data seeding loads a known dataset via SQL, factories, or API calls so assertions target predictable values. A factory is a helper that generates valid, randomized test objects so each test gets fresh, isolated data. Common tooling for everyday work includes Postman (a saved collection of requests, variables, tests, and environments), Newman (its command-line runner for headless CI), curl (invaluable for quick probing and bug reproduction), and HTTPie (a more humane command-line client). For methods, prefer curl -d with an explicit Content-Type header over curl -X POST, which forces the method without setting defaults reliably.

All chapters
  1. 1Foundations of API Testing
  2. 2HTTP Status Codes and Communication
  3. 3HTTP Methods, Headers, and Request Formats
  4. 4Authentication, Authorization, and Browser Concerns
  5. 5API Architectural Styles and Security
  6. 6Contract Testing, Mocks, and Test Patterns
  7. 7Performance, Observability, and Operations

Drill it

Reading is not remembering. These come from the API Testing deck:

Q

What is API testing?

API testing verifies that an application's interfaces behave correctly in terms of functionality, contracts, performance, security, and error handling.

Q

Why is API testing valuable?

It catches issues below the UI layer, runs faster than end-to-end tests, and provides direct confidence in service behavior.

Q

What is the difference between unit, integration, and API tests?

Unit tests isolate small pieces of logic, integration tests verify components working together, and API tests validate behavior at the service boundary.

Q

What is a contract in API testing?

A contract describes the expected structure and behavior of requests and responses, including fields, types, and status codes.