Test an integration end to end
An integration that works on the happy path is half an integration. This is the other half — the tests that decide whether a bad night costs ten minutes or a week.
Test with fixtures, not with today's data
A fixture is a record you create deliberately so that a test is repeatable. The alternative — testing against whatever happens to be in the system today — gives you results nobody can reproduce next week.
The fixtures worth having in the sandbox:
| Fixture | Tests |
|---|---|
| An order in each status | That the ERP reads pending, placed, in_fulfillment, completed and cancelled correctly |
An order with a customer_order_number | That the buyer's PO number survives into the ERP |
| An order spanning two cost centres | That per-position data is not dropped |
| An order with a part-shipped position | The three statuses disagreeing on purpose |
| An article sold in packs of 12 | Quantity and unit handling |
| An article with a comma decimal price | Locale handling in files |
| A blocked organization | That checkout refuses |
| An organization with an approval threshold | That an order can legitimately arrive as pending |
An article with zero available but positive on_hand | Reservation logic, the classic phantom stock-out |
| A discontinued article on a saved list | Conversion behaviour when an article disappears |
Build them once, keep them as part of the sandbox seed, and re-create them on every refresh.
The tests most projects skip
Four, and each one corresponds to a real production incident.
1. The idempotency test
What to do: send the same order to the ERP twice — same event, same order number. Then run the same import file twice.
What must happen: one order in the ERP. One set of records after the import, not two, and no error.
This matters because there is no Idempotency-Key header on this platform.
Idempotency is a property of your data, not of a request header, and it rests on
two things: matching on a stable business key (SKU, customer number, order
number — never an internal id), and upserting rather than inserting. Every
import should mean "make the record look like this".
The classic failure this catches is a stock file applied as a delta rather than a state. Run it twice and stock is wrong by exactly one night's movements, silently, with nothing in any log.
2. The timeout test
What to do: cut the connection mid-request on a write, or point the integration at an endpoint that accepts the connection and never answers.
What must happen: the integration retries and the result is still one order, not two.
A 429 means nothing happened and is always safe to retry. A timeout tells
you nothing about whether the other side processed the request — and that is
precisely the case idempotency exists for. An integration that treats those two
identically is one that will produce duplicate deliveries.
3. The replay test
What to do: in the webhook's Delivery attempts, redeliver an event the receiver has already processed.
What must happen: the receiver deduplicates on the event id and does
nothing. No second order, no error, a 2xx response.
Delivery is at-least-once. The same event will arrive twice eventually,
usually on a night when somebody is redeploying the receiver. Then run the
negative: replay a delivery with a corrupted signature and confirm the receiver
answers 401 and does not process it.
4. The receiver-down test
What to do: stop the receiver for longer than the whole retry window, then bring it back.
What must happen: the platform's delivery log shows the failures, the destination may be disabled automatically, and — the point of the test — your reconciliation run finds the orders that were missed.
If the answer to "what happens to the orders that never arrived?" is a shrug, the integration is missing its safety net. A webhook is the fast path; a scheduled reconciliation is the guarantee. See Common API tasks, by job.
Volume and locale
Two more that catch a specific class of go-live failure:
Run the real volume once. Not 3,000 sandbox products — your actual 400,000. The rate limit is per tenant and shared, and a job that is fine at sandbox size can consume your whole allowance at production size. See Rate limits, quotas and fair use.
Test one file with German-locale numbers. 19,90 read by a system expecting
19.90 produces prices wrong by a factor of a hundred, with no error anywhere.
Also test an umlaut in a company name and a ;-separated CSV.
The go-live checklist
Hold the project to this. Every line has a yes/no answer and a name against it.
Access
- Production keys created, scoped to exactly what the integration uses — not
* - Secrets stored in a secret manager, not in email, chat or a ticket
- Key inventory updated: which key, which integration, which owner, who to call
- Sandbox keys revoked, or scheduled for revocation
Data
- Field ownership agreed and written down — see Deciding your system of record
- Import profiles map only the fields the source owns
- Every match runs on a business key, never an internal id
- Codes identical between sandbox and production —
thread_size, notthread_size_1
Behaviour
- Idempotency test passed, both directions
- Timeout test passed
- Replay test passed, including the bad-signature case
- Receiver-down test passed, and reconciliation found the gap
- Retry policy documented: how many attempts, how long, what happens after
Operations
- Alerts route to a team address, never to a person
- "No file arrived by 03:00" alarm exists, not only "the run failed"
- Somebody is named as owner of each integration
- The runbook says what to do when it breaks at 02:00 on a Sunday
-
X-Request-IDis logged on every call - A rollback plan exists: how to stop the integration without stopping the shop
The first week
- Somebody reads the API and delivery logs daily, not weekly
- Reconciliation is run manually on day one and compared by hand
- The first full-volume nightly run is watched by a person
What to check afterwards
For the first two weeks, daily:
- Error rate by status code — see Monitor your integrations.
- The webhook failed queue is empty.
- Reconciliation finds zero missing orders.
- No integration is retrying constantly and succeeding, which is a limit you are quietly living against.
Next
- Monitor your integrations — the daily read from here on.
- Handling API changes — keeping it working after go-live.