mail@mabbaz.com Abu Dhabi, UAE

Enterprise Integration · Practice · Testing

Integration Testing Checklist

Integrations rarely fail inside a system. They fail in the seams between systems, in the space where one application hands data to another and assumes it will be understood. That seam is exactly where testing is hardest to set up, easiest to skip, and most expensive to get wrong. This is a practitioner's checklist for testing integrations properly, layer by layer, so the failures surface in your test environment instead of your production one.

Muhammad Abbas July 10, 2026 ~12 min read

In more than twenty years of building and untangling enterprise integrations, I have learned that the code inside each system is almost never the problem. The ERP works. The CAFM works. The payment gateway works. What breaks is the assumption one system makes about another: a field that used to be a string is now a number, a date format that shifted, a status code nobody documented, a retry that fired twice and created two purchase orders. These are integration defects, and they live in the gaps that unit tests never touch. This article is the checklist I use to hunt them down before they reach a customer.

Start with the architecture, then test it. This checklist assumes you already understand what your integration is supposed to do and how the pieces connect. If you are still designing that, read the pillar first: Enterprise system integration explained. Testing is how you prove the architecture behaves. It is not a substitute for having one.

1. Why integration testing is different

Most developers arrive at integration testing with habits formed by unit testing, and those habits actively mislead them. A unit test isolates one function, feeds it known inputs, and checks the output. It is fast, deterministic, and entirely under your control. Integration testing is none of those things. It involves at least two systems that you may not both own, that run on their own release schedules, that hold state between calls, and that can be unavailable, slow, or subtly wrong at any moment. The variables you cannot control are precisely the ones that cause production incidents.

The first difference is ownership. When your ERP talks to a third-party logistics provider, you control one side of the conversation and merely hope about the other. Their API can change without notice, their sandbox can behave differently from their production, and their documentation can lie. Integration testing has to account for a counterparty that is not on your team and does not share your priorities.

The second difference is state. A unit is usually stateless: same input, same output, every time. An integration carries state across the boundary. Creating a record on one side changes what the other side will return next. A test that passes in isolation can fail when run after another test that left data behind. This is why integration test suites need deliberate setup and teardown, and why "it worked when I ran it alone" is not evidence of anything.

The third difference is failure modes. A function either returns or throws. An integration can succeed slowly, succeed partially, time out after doing the work, return a success code with a garbage body, or fire twice because a retry could not tell whether the first attempt landed. These are the failures that hurt most, and they are invisible to a testing approach that only checks the happy path. The rest of this checklist is organised around finding them, one layer at a time. For the design principles that make these layers testable in the first place, see how to design integration architecture.

2. Contract testing

The cheapest integration bug to catch is the one where two systems simply disagree about the shape of the data. The consumer expects a field called customerId as a string; the provider renames it to customer_id and makes it an integer. Nothing is technically broken on either side. Each system passes its own tests. The integration is dead. Contract testing exists to catch exactly this class of failure, and it catches it early, before you ever stand up both systems together.

A contract is a shared, machine-checkable definition of the interface between a consumer and a provider: the endpoints, the request shape, the response shape, the status codes, the required fields, and their types. Contract testing verifies that both sides honour that definition independently. The consumer proves it sends what the contract promises and can handle what the contract returns. The provider proves it accepts valid requests and produces responses that match. Neither test needs the other system to be running, which is what makes contract tests fast and reliable.

The pattern most teams reach for here is consumer-driven contract testing, popularised by the Pact family of tools. The idea is that the consumer defines its expectations as a contract, and the provider verifies against that contract in its own pipeline. If the provider makes a breaking change, the provider's build fails, not the consumer's production traffic. This inverts the usual failure timing: the break surfaces in the team that caused it, at the moment they cause it, instead of days later in someone else's incident channel.

Contract testing does not prove the integration works end to end. It proves the two sides agree on the interface, which is a necessary condition for the integration to work and a common reason it does not. Treat it as the foundation layer: fast, cheap, and run on every commit.

3. Integration testing

Once each side honours the contract, the next layer verifies that the real components actually talk to each other. This is integration testing in the narrow sense: you connect your system to a real or realistic instance of the other system and exercise the actual calls, over the actual protocol, with the actual serialisation. Contract tests use recorded expectations; integration tests use live wires.

The value here is that a great many defects only appear when real infrastructure is involved. Authentication headers that were assumed rather than tested. TLS negotiation that fails against the provider's real certificate chain. Timeouts that were fine against a local mock but too tight against a service two regions away. Character encoding that survived the mock but mangled a customer name with an accented letter against the real endpoint. Connection pooling that leaked under sustained calls. None of these show up in contract tests because contract tests deliberately remove the network. Integration tests put it back.

The practical challenge is what to integrate against. A live third-party production system is off limits for testing. A vendor sandbox is the ideal when it exists and is faithful, but sandboxes are notorious for behaving differently from production, so treat sandbox results as indicative rather than authoritative. For systems you control, a dedicated test instance is the right answer. For systems where no faithful test target exists, a well-maintained mock or a service virtualisation layer that replays realistic responses is the pragmatic compromise, provided you understand its limits. If your integration involves Microsoft Dynamics 365 Business Central, the specifics of its API surface and authentication are worth studying directly; I cover them in Business Central APIs and integrations.

4. End-to-end testing

Above integration testing sits the end-to-end layer, where you test a complete business process across every system it touches, in sequence, the way a real transaction would flow. A purchase requisition raised in the CAFM becomes a purchase order in the ERP, triggers a goods receipt, matches an invoice, and posts to the general ledger. An end-to-end test drives that entire chain and asserts that the final state is correct in every system along the way.

End-to-end tests are the most convincing evidence that an integration works, and the most expensive to build and maintain. They are slow, because they wait for real processing. They are brittle, because a change in any system along the chain can break them. They are hard to diagnose, because a failure at the end could originate anywhere in the middle. For all these reasons, end-to-end tests should be few and precious. You do not test every field permutation here; you test that the critical business journeys complete and produce the right outcome.

The discipline that keeps end-to-end testing sane is choosing the right journeys. Pick the handful of processes that carry the most business value or the most risk: the order-to-cash flow, the procure-to-pay flow, the flows that touch money or safety or compliance. Test those thoroughly and repeatedly. Resist the temptation to push edge-case coverage up to this layer, where it is slow and fragile; edge cases belong lower down, in contract and integration and dedicated error-case tests, where they run fast and pinpoint the fault precisely.

A caution on end-to-end obsession. Teams that skip the lower layers and rely on a big suite of end-to-end tests end up with a slow, flaky pipeline that everyone learns to ignore. When an end-to-end test fails, nobody can tell whether the code is broken or the test is flaky, so failures get rerun until they pass. That is worse than no test at all, because it manufactures false confidence. Put most of your coverage in the fast, precise lower layers and use end-to-end tests sparingly to confirm the whole chain holds together.

5. Error and edge cases

This is the layer that separates integrations that survive contact with production from those that do not, and it is the layer most teams skip. The happy path is easy: valid request, valid response, everyone goes home. Real integrations spend a meaningful fraction of their life on unhappy paths, and untested error handling is where the expensive incidents come from.

The essential error scenarios to test deliberately, not just hope about:

  • The other system is down. What does your integration do when the endpoint returns a connection refused or a 503? Does it queue and retry, fail loudly, or silently drop the message? Test it by pointing at a dead endpoint and observing the behaviour.
  • The other system is slow. A response that arrives after your timeout is a special kind of danger, because the work may have completed on the far side while your side treats it as failed. Test with artificial latency past your timeout threshold.
  • Malformed or unexpected responses. A 200 status with an empty body, a truncated payload, or a field that is null where you expected a value. Your parser should degrade gracefully, not crash the whole flow.
  • Duplicate delivery. If a message can be delivered twice, and in any at-least-once system it can, does processing it twice create two records or one? This is the idempotency test, and it is non-negotiable for anything that creates or moves money.
  • Partial failure. A batch of fifty records where record twenty-three is rejected. Does the whole batch roll back, does it commit forty-nine and report one, or does it commit the first twenty-two and abandon the rest? Any of these can be correct; silently doing something different from what you assumed is not.
  • Out-of-order and stale data. An update arriving before the create it depends on, or an old event overtaking a newer one. Test that your ordering and versioning assumptions actually hold.

Testing error paths well requires the ability to inject faults on demand: force a timeout, return a 500, drop a connection, replay a duplicate. Build that capability into your test harness deliberately. The whole discipline of designing these behaviours correctly is a subject in its own right, and I treat it fully in integration error handling best practices. Testing is how you prove that error handling design actually works under the conditions it was built for.

6. Performance and load testing

An integration that works correctly for one transaction can still fail catastrophically for ten thousand. Performance and load testing verify that the integration holds up under realistic and peak volume, and they routinely expose problems that functional testing never touches.

The scenarios worth measuring: sustained throughput at expected daily volume, burst behaviour at peak (the month-end batch, the morning rush of work orders, the seasonal spike), and behaviour as the backlog grows. Watch for rate limits, because most modern APIs impose them and an integration that ignores them will start receiving 429 responses under load and, if it has not been tested for that, will treat throttling as failure and make things worse by retrying aggressively. Watch for connection and thread exhaustion, memory growth over long runs, and database contention on the systems at either end.

Two failure patterns deserve special attention because they only appear under load. The first is the retry storm: a downstream slowdown causes timeouts, timeouts cause retries, retries add load, load causes more slowdown, and the system spirals. Test that your retry logic includes backoff and a circuit breaker so a slow dependency degrades gracefully instead of collapsing. The second is queue growth: if messages arrive faster than they can be processed, the backlog grows without bound. Test what happens when the queue is deep, and make sure the system recovers when the pressure eases rather than falling permanently behind. Both of these are far easier to observe if the integration is well instrumented, which is the subject of integration monitoring and logging.

7. Security testing

Every integration is an attack surface. It moves data across a trust boundary, often carrying credentials, personal information, or financial records, and it is frequently the least scrutinised part of a system because it sits between teams rather than inside one. Security testing for integrations covers a few distinct concerns.

Authentication and authorisation come first. Verify that the integration cannot be called without valid credentials, that expired or revoked tokens are rejected, and that a caller authenticated for one scope cannot reach data outside it. Test the token refresh path, because an integration whose credentials silently expire is a self-inflicted outage waiting to happen. Verify that secrets are not logged, not hard-coded, and not passed in URLs where they land in access logs and browser history.

Transport security comes next. Confirm that traffic is encrypted in transit, that certificate validation is actually enforced rather than disabled to make development easier and then forgotten, and that the integration refuses to fall back to an unencrypted channel. Then comes input handling: an integration endpoint accepts data from another system, and that data must be validated and sanitised exactly as rigorously as data from a user, because a compromised or misbehaving upstream system can send malicious payloads. Test for injection through integration fields, oversized payloads, and unexpected content types. Finally, verify that error responses do not leak internal detail such as stack traces, internal hostnames, or database structure to the far side of the boundary.

8. Test data and environments

None of the testing above is trustworthy if the data and environments underneath it are wrong, and this is where integration testing quietly falls apart in practice. Integration tests need realistic data on both sides of the boundary, kept consistent, and environments that resemble production closely enough that results transfer.

The data problem is that an integration test often needs matching reference data in two systems: the same customer, the same product, the same cost centre, present and consistent in both the ERP and the CAFM. Keeping those in sync across test environments is real work, and when they drift, tests fail for reasons that have nothing to do with the code. Invest in seeding and teardown that establish a known state before each run and clean up after, so tests are repeatable and independent. A test that depends on data left behind by a previous test is a test that will eventually lie to you.

The environment problem is fidelity. A test environment that differs from production in configuration, version, network topology, or scale will pass tests that production then fails. Vendor sandboxes drift from vendor production. Test databases hold a thousand records where production holds ten million, hiding performance problems. Firewall rules differ. The more your test environment resembles production, the more your test results mean. Where perfect fidelity is impossible, know the differences and account for them rather than pretending they do not exist. And never test against production systems you do not own, and be extremely careful testing against your own production, because integration tests create and modify data and a stray test run against live systems is its own kind of incident.

9. The checklist

The layers above form a stack, and the discipline is to put most of your test coverage at the bottom, where tests are fast, cheap, and precise, and less at the top, where they are slow, expensive, and broad. The shape below is the integration testing equivalent of the testing pyramid: a wide base of contract tests, a solid middle of integration and error-case tests, and a narrow apex of end-to-end tests, with performance and security as cross-cutting concerns validated against the whole stack.

The integration testing stack Most coverage at the base (fast & cheap), least at the apex (slow & broad) End-to-end Performance & security Error & edge cases Integration (live wires) Contract testing speed & volume of tests increases downward Unit tests sit below this stack, inside each system, before any boundary is crossed

The table below turns the stack into a working checklist. Each row is a test type, what it verifies, and a concrete example drawn from a typical ERP-to-CAFM integration. Use it as a coverage map: for any integration you own, you should be able to point to real tests in every row.

Test type What it verifies Example
Contract Both sides agree on request and response shape, fields and types Provider renames customerId to customer_id; the provider build fails, not production
Integration Real components connect over the real protocol with real auth and encoding A work order posts to the ERP with a valid OAuth token and an accented customer name survives intact
End-to-end A full business process completes correctly across every system it touches Requisition to PO to goods receipt to invoice match to GL posting, end state correct everywhere
Error & edge Graceful behaviour on downtime, timeout, malformed data, duplicates, partial failure A duplicate message is delivered twice and creates one purchase order, not two
Performance Throughput at peak, rate-limit handling, backoff, no retry storms or unbounded queues Month-end batch of 20,000 records completes; 429 responses trigger backoff, not aggressive retries
Security Auth enforced, transport encrypted, input validated, no secret or detail leakage A call with an expired token is rejected; error responses reveal no stack trace or hostname
Data & env Consistent reference data both sides, repeatable seeding and teardown, production-like fidelity The same cost centre exists in ERP and CAFM before the run; state is reset afterwards

The point of the checklist is not to run every row on every commit. Contract and integration tests run on every change. Error-case tests run on every change to the integration itself. End-to-end, performance, and security tests run on a slower cadence, before releases and on a schedule. What matters is that every row is covered by something real, so that when an integration breaks in production you can say honestly that it was an untested scenario rather than an untested layer.

10. References

The techniques in this article draw on well-established testing practice rather than any single source, but a few concepts are worth naming so you can study them further.

  • Consumer-driven contract testing and the Pact concept. The idea that a consumer defines its expectations of a provider as a versioned contract, and the provider verifies against that contract in its own pipeline, comes from the consumer-driven contracts pattern and is embodied in the Pact family of open-source tools. Search for consumer-driven contract testing and Pact to find the current documentation.
  • The testing pyramid. The principle that automated test suites should have many fast, low-level tests and few slow, high-level ones is a long-standing idea in software testing, usually attributed to the testing pyramid model. It applies to integration testing exactly as it applies to unit and UI testing.
  • Idempotency and at-least-once delivery. The reasoning behind idempotency testing comes from the semantics of message delivery in distributed systems, where at-least-once delivery is common and exactly-once is difficult, making idempotent processing the practical safeguard against duplicate side effects.

None of these require a specific product. They are patterns you can apply with whatever tooling your stack already uses. The value is in the discipline, not the tool.

Final thoughts

Integration testing gets skipped because it is genuinely harder than unit testing. It needs two systems, realistic data, controllable failures, and environments that resemble production. Standing all of that up is real work, and under deadline pressure it is the work that gets cut first, on the reasonable-sounding logic that each system already passes its own tests. That logic is exactly wrong. Each system passing its own tests is precisely the situation in which integration defects hide, because the defect is not in either system. It is in the agreement between them.

The checklist in this article is not a bureaucratic box-ticking exercise. It is a map of where integrations actually fail, ordered from the cheapest failures to catch to the most expensive. Start at the bottom with contract testing, because interface disagreements are the most common and the easiest to prevent. Build up through integration and error-case testing, which is where the incidents that wake people up at night are prevented. Cover performance and security as cross-cutting concerns, and never trust results built on unrealistic data or environments. Do that, and the failures surface in a test run where they are cheap. Skip it, and they surface in production, in the seam between systems, at the worst possible moment, exactly as they always have.

Building or inheriting a critical integration?

Independent review of integration architecture, test strategy, error handling and monitoring across ERP, EAM, CAFM and third-party systems. 22+ years connecting enterprise systems in utilities, oil and gas, manufacturing, government and facility operations. Vendor-neutral, focused on the seams where integrations actually break.

Book a conversation

Related reading: Enterprise system integration explained, How to design integration architecture, Integration error handling best practices, Integration monitoring and logging, Business Central APIs and integrations.

Muhammad Abbas

CMMS / CAFM Manager & Enterprise Integration Specialist · 22+ years across ERP, EAM, CAFM and enterprise integration.

Work with me
MAbbaz.com
© MAbbaz.com