How-to guidesMock and contract-test an API
Mock and contract-test an API
A contract is only useful while both sides keep it. This guide serves an API from its OpenAPI
document, openapi.yaml, so that client teams can start before the server exists, and then checks
the real server’s traffic against the same document, locally and in CI. apigenie-server does the
same for AsyncAPI documents (WebSocket, HTTP and MQTT), .proto files (gRPC) and GraphQL schemas;
see the server reference.
You need the CLI installed; the Quickstart shows how.
Serve a mock
apigenie-server openapi.yaml
Listening on http://127.0.0.1:4010/v1 (spec: openapi.yaml, 12 operations)
Admin API: http://127.0.0.1:4010/__apigenie
Dashboard: http://127.0.0.1:4010/__apigenie/ui
The mock serves the paths of the document under the path of its first server (/v1 here), checks
every request against the document, and answers with the document’s examples. Operations without
examples get a body generated from their schemas; --dynamic generates every answer.
--watch reloads the mock when the document changes, so it follows the contract as you write it.
Open the dashboard to see each request, what answered it and what was wrong with it.
Ask for the answers you need
A client has to handle more than the happy path. The Prefer header, or a query parameter, picks
the answer of one request:
curl http://127.0.0.1:4010/v1/orders -H 'Prefer: code=404'
curl http://127.0.0.1:4010/v1/orders -H 'Prefer: example=empty'
curl http://127.0.0.1:4010/v1/orders -H 'Prefer: delay=1500'
For answers that should hold for a while, such as an outage of one service, define scenarios in the
document or in the configuration, and switch them on with --scenario or in the dashboard:
paths:
/orders:
get:
x-apigenie-mock:
scenarios:
empty: {body: []}
broken: {status: 500, drop: {probability: 0.5, mode: reset}}
--latency, --jitter and --drop-rate make every answer slow or unreliable, and --seed repeats
the same run.
Check the real server
Once the server runs, put apigenie-server in front of it with --proxy-to. Every request goes to
the real server, and the request and the server’s response are both checked against the document:
apigenie-server openapi.yaml --proxy-to http://localhost:8080
Point your tests, your front end or a load test at the proxy instead of the server, and their
traffic becomes a contract test. Each response carries the problems found in its exchange in the
X-Apigenie-Mock-Violations headers, and the dashboard’s Contract screen shows the coverage and the
violations by check.
To send the requests yourself rather than through your tests, apigenie test runs .http files,
or one generated request per operation, against the server with the same checks:
apigenie test --spec openapi.yaml --generate --base-url http://localhost:8080
Fail CI when the contract breaks
In CI, start the proxy, run the test suite through it, and stop it. On stopping it writes the report and exits with status 5 when the contract failed:
apigenie-server openapi.yaml --proxy-to http://localhost:8080 --port 4010 \
--report contract.json --report-junit contract.xml --fail-on errors --min-coverage 80 &
API_URL=http://localhost:4010 npm test
curl -X POST http://localhost:4010/__apigenie/shutdown
wait $!
--fail-on errors fails on any error-level violation, and --min-coverage 80 when the tests leave
more than a fifth of the document untested. Most CI systems show the JUnit report, one test case per
operation, next to the other test results.
Record once, replay in tests
Integration tests that call other services are slow and flaky. --record DIR writes every exchange
of a session to a directory, and --replay DIR answers from it later, so the tests run without the
real services:
apigenie-server openapi.yaml --proxy-to https://api.example.com --record sessions/checkout
apigenie-server openapi.yaml --replay sessions/checkout
See session recording for what is recorded and how secrets are kept out of the files.
Next steps
- Server reference: stubs, scripts, the document store, several services and TLS.
- apigenie test:
.httpfiles, assertions and scripts. - Mock and test: what the server covers.