My app integrates with several external APIs. Some provide sandboxes, while others do not, and even the available sandboxes often differ from production in undocumented ways. The main approaches I've considered are running tests against a real sandbox, recording and replaying responses, writing mocks by hand, and using contract-testing tools. Each has obvious weaknesses: sandboxes are slow, rate-limited, and usually lack realistic data; recorded fixtures become stale; hand-written mocks can encode misunderstandings of the API; and contract testing is difficult when the provider is outside your organization.
I'm particularly interested in what teams actually settle on in practice. Do you run scheduled checks against the real APIs to detect drift? How do you test pagination, large datasets, nullable fields, and different response states? Do you use realistic-volume mock data, or accept that some performance and pagination issues will only appear in production? And have you found any vendors with genuinely reliable sandbox environments?
3 Answers
For straightforward integrations, we usually capture a handful of real responses and store them as JSON fixtures. The application code is tested against those locally, while a nightly sandbox run exercises a few representative calls. It isn't perfect, and sometimes drift is still discovered in staging or through a customer report, but it gives us useful coverage without putting external calls in every test run.
A single happy-path recording isn't enough. Responses can vary based on permissions, nullability, pagination state, resource status, and error conditions, so the fixture set needs to cover the states the application actually depends on.
The right level of testing depends on the integration. For a simple one-way event, the important pieces may just be serialization, retries, logging, and monitoring failed deliveries. A full remote dataset may not be worth maintaining if the provider gives clear versioning and failures are visible immediately.
For deeper integrations—especially payments or anything that changes remote state—a dedicated vendor account or sandbox is much more important. Some providers offer good test environments, while others require negotiating a separate account and carefully isolated data. Maintaining realistic environments is expensive, so many teams accept that a few edge cases will only be found outside local development.
A practical setup is to keep fast, deterministic fixtures for the normal test suite, but also run a small canary against the real provider on a schedule. The canary can validate important response shapes with JSON Schema or OpenAPI and alert on breaking changes such as removed fields, changed types, or newly required values. It should be outside the pull-request test suite, so a provider outage or quota problem doesn't block development, but it can still reveal when fixtures have stopped matching reality.

That separation is the part I was missing: fixtures make CI reliable, while the scheduled check provides the signal that the fixtures are becoming stale. I'd also need several requests covering different resource states rather than treating one recorded response as the whole contract.