API Contract Testing
1) Why contract testing
The contract captures customer expectations and provider promises: routes/methods, headers, body schemas, statuses, error semantics, and constraints. The goal is to catch incompatible changes before integration and release versions safely, without heavy E2E.
2) Approaches
Consumer-Driven Contracts (CDC): the client forms expectations (Pact and analogues); the provider regularly verifies them.
Specification: contract as a single source of truth (OpenAPI/Protobuf/GraphQL SDL); tests validate implementation against specification.
Event-based: message schemas (Avro/JSON Schema/Protobuf) + broker/registry compatibility rules.
3) HTTP/REST specification stream
1. Contract: OpenAPI 3. x (diagrams, examples, codes).
2. Lint and stat analysis: style, required fields, uniform codes.
3. Implementation validation: anti-OpenAPI test generator (schemathesis/Dredd approach) + manual negatives.
4. Snapshots: capture sample responses, ETag semantics, idempotency headers.
yaml paths:
/v1/payments:
post:
responses:
"201": { $ref: "#/components/responses/PaymentCreated" }
"409": { description: Duplicate by Idempotency-Key }
"422": { description: Schema/Business validation failed }
4) CDC (Pact approach)
Lifecycle:1. Consumer writes a test, generates a pact file (expectations).
2. Publication in a broker (artifact).
3. Provider in CI raises service (or contract-rack), verifies pact's.
4. The broker calculates the can-i-deploy compatibility matrix.
Example of a consumer test (pseudo-JS):js pact
.given("wallet exists")
.uponReceiving("get wallet")
.withRequest({ method:"GET", path:"/v1/wallets/w123", headers:{ "Authorization": term({generate:"Bearer x", matcher:/^Bearer\s.+/}) }})
.willRespondWith({
status: 200,
headers: { "Content-Type": "application/json" },
body: like({ id:"w123", currency: "EUR", balance: 0 })
});
5) gRPC/Protobuf contracts
The contract is' .proto'with package/service versioning.
Compatibility: do not reuse tags, only add new ones with optional, do not delete fields used; reserve numbers.
Tests: server/client stab generation, auto-generated case rental + negatives (unknown fields, size-limits).
6) Event Contracts (Kafka/NATS/...)
Схемы: Avro/JSON Schema/Protobuf в Schema Registry.
Compatibility policy is'BACKWARD '(often enough) or'FULL'.
Producer tests: validates the message against the scheme; consumer tests: accepts old and new versions.
Invariants: idempotence keys, order/repeatability, deduplication semantics.
json
{"type":"record","name":"PayoutCreated","fields":[
{"name":"payoutId","type":"string"},
{"name":"amount","type":"double"},
{"name":"currency","type":{"type":"string","logicalType":"iso-4217"}}
]}
7) Compatibility and versions
Backward-compatible (recommended minimum): add optional fields, do not break existing ones.
Forward-compatible: Consumers ignore unknown fields.
Full: Both.
Versioning: 'path (/v1)', 'Accept: application/vnd. brand. v2+json`, `proto package v2`.
Deviation policy: output window (for example, 90 days), warning headers/events.
8) Negative and mistakes are also a contract
Standardize codes: 400/401/403/404/409/422/429/5xx, mandatory fields' code ',' message ',' trace _ id '.
Sizes/limits are part of the contract (413/414/431).
Idempotency: repetition behavior (409 vs 201 same id).
Headings: 'Retry-After' at 429/503, 'Idempotency-Key', 'Content-Language', etc.
json
{ "code":"validation_error", "message":"amount must be ≥ 1", "trace_id":"..." }
9) Data managed by contract
Examples - live, validated in CI.
Fixtures for CDC are minimal, deterministic.
Data generation - property-based for numbers/dates; but not to break the stability of snapshots.
10) Pipeline in CI/CD (reference)
1. Lint/validate: OpenAPI/Proto/Avro (`validate`, style-линер).
2. Publish: Contract as artifact (broker/registry).
3. Verify: the provider runs CDC packets/specification tests.
4. can-i-deploy gate: no release allowed without green matrix.
5. Diff-Verifies that the changes are semantic diff.
6. Report: JUnit/HTML, list of violations/definitions.
yaml jobs:
lint:...
publish-contract: needs: [lint]
verify-provider: needs: [publish-contract]
can-i-deploy:
if: always()
steps: [run: pact-broker can-i-deploy...]
11) Tools (by task class)
Lint/validators: openapi-linters, protobuf-lint, avro-tools.
CDC: Pact family/broker, Spring Cloud Contract, Hoverfly (HTTP records/replays).
Specification runners: schemathesis/Dredd-like approach, Postman test + JSON Schema.
Diff: semantic-diff OpenAPI/Proto/Avro (detects breaking changes).
Containers: Testcontainers to raise provider/contract desk.
12) Antipatterns
"Special dock" separately from the code → desynchronization. Keep the contract near the service.
Moki of a third-party service instead of a contract → fragility during updates.
Random responses/generation without binding to the → flake scheme.
Changing types/mandatory fields without version.
Silent extensions without notification/decrement.
No negative contracts and error codes.
13) Specifics of iGaming/Finance
Formalize money fields: 'amount' - decimal with scale, currency - ISO-4217, invariants of amounts.
Payment/webhook contracts: HMAC/mTLS, anti-replay ('X-Timestamp' window), idempotency, 'Retry-After'.
Regionality/tenants: mandatory headers' X-Tenant/X-Region ', localization of messages.
Events: unchanging logs (audit), deduplication keys, delivery guarantees (at least once + idempotent handlers).
14) Examples of "skeletons" of tests
14. 1 Schemathesis-style (pseudo)
bash schemathesis run openapi. yaml --checks all --hypothesis-deadline=200
14. 2 Postman as spec runner
js pm. test ("Scheme/v1/wallets/{ id} is valid," () => {
const schema = pm. collectionVariables. get("wallet_get_schema");
pm. expect(ajv. validate(JSON. parse(schema), pm. response. json())). to. be. true;
});
14. 3 CDC Provider Verification (Pseudo)
bash pact-broker can-i-deploy --pacticipant wallets --version $SHA --to-environment staging
15) Prod Readiness Checklist
- Contracts in repository, CI validates and publishes artifacts.
- CDC enabled for critical integrations; broker/matrix "can-i-deploy" works.
- Compatibility policy (HTTP/gRPC/Events) documented; automatic semantic-diff.
- Negative contracts: errors, size limits, idempotence, 'Retry-After'.
- Test data is deterministic; snapshots of examples are supported.
- Versioning and deprecation: deadlines, notifications, headers.
- For events - Schema Registry and compatibility mode; The producer/consumer is testing both versions.
- Artifacts: JUnit/HTML, diff/verify reports, compatibility matrix.
- Incident procedure: fast contract rollback/feature flag, notification to integrators.
16) TL; DR
Capture expectations in contracts and run them automatically: CDC for customer expectations, specification tests to match implementation, schema register for events. Keep strict compatibility policies and negative contracts (errors, limits, idempotence). You cannot release without a green can-i-deploy matrix and semantic-diff without breaking changes.