Use when designing tests that verify the interaction between two or more units of a system — modules, services, layers, processes: the scope-and-boundary primitives that distinguish integration from unit and e2e tests, the test-pyramid (Cohn 2009) and test-trophy (Dodds) frameworks for how much integration testing belongs in the suite, the real-vs-faked-collaborator decision per dependency, the test-data lifecycle (per-test setup, transaction rollback, container reset), the difference between sociable-unit tests, integration tests, and contract tests, and the failure modes (over-broad scope that mimics e2e, over-narrow scope that mimics unit, shared mutable state that produces flakes). Do NOT use for testing one unit in isolation (use testing-strategy + test-doubles-design), full user-journey testing (use e2e-test-design), consumer-driven contract verification (use contract-testing), or test-suite quality measurement (use mutation-testing).
Integration test design verifies the interaction between two or more units of a system — modules within a process, services across processes, layers within an architecture — to catch defects that emerge only at the boundaries between those units. The five primitives are: boundary (module-to-module, layer-to-layer, service-to-database, service-to-message-bus, service-to-third-party, service-to-service), scope (which collaborators are real, which are faked, which are out of scope), real-vs-faked-collaborator decision per dependency (real where the boundary's failure modes are integration-bug-finders — database, message bus, cache; faked where the realness adds cost without proportional defect-detection — paid third-party APIs, email/SMS providers), test-data lifecycle (full reset, transaction rollback per test, container reset, shared snapshot with no-mutation discipline), and pyramid-vs-trophy framing (Cohn 2009: many unit, fewer integration, fewest e2e; Dodds 2018: many integration, fewer unit, fewer e2e, static-analysis stem — integration-heavy when modern tooling makes integration cheap).
Replaces "comprehensive unit tests covering each unit in isolation" as the sole verification strategy with deliberate seam-verification. Solves the problem that a test suite of comprehensive unit tests has verified each unit but not the system — the seams are unverified, and most production failures happen at seams (database transaction boundaries, message-bus delivery semantics, third-party API contract changes, configuration drift between environments). Modern testing infrastructure has shifted integration-test cost down enough that the test trophy framing (integration-heavy suite) has gained ground on the pyramid (unit-heavy suite); the right ratio for any given codebase depends on which suite costs are real (slow tests in CI) and which are surmountable with infrastructure (containerized dependencies, transaction rollback, parallelization).
Distinct from testing-strategy, which owns the strategic ratio question (how much of each level) — this skill owns the design of integration-level tests specifically. Distinct from test-doubles-design, which owns the construction of mocks/stubs/fakes as constructs — this skill owns the per-dependency real-vs-faked decision in integration scope (integration tests use real where practical, fakes only at true external boundaries; mocking the database in an "integration test" is the most common scope failure). Distinct from e2e-test-design, which owns user-journey-scope tests through the full stack including UI — this skill owns the scope below that, interaction of units inside the system, often without UI. Distinct from contract-testing, which owns consumer-driven contract verification between services — contract tests verify the interface; integration tests verify the implementation through the interface; the two compose, one does not replace the other. Distinct from mutation-testing, which is a test-suite quality measurement applied at any level — this skill is the design of integration-level tests themselves. Distinct from snapshot-testing, which is a capture-and-compare technique applicable inside any test level. An integration test is to a software system what a fire-suppression drill in a specific corridor is to the whole building's safety plan — you are not testing whether each sprinkler head works in isolation (unit), nor whether everyone evacuates the entire building in fifteen minutes (e2e), you are testing whether the smoke detector in this corridor triggers the alarm panel which triggers the sprinkler which actually wets that carpet; the test's identity is the named boundary, and changing the named boundary changes the test's identity. The wrong mental model is that an integration test is "a unit test with more stuff in it" or "an e2e test with the UI removed." It is neither. Scope failures are the dominant source of fragile integration suites. Too narrow (mocks at the actual boundary): the "integration test" is a unit test in disguise and misses the integration bugs the technique exists to catch — type misalignment, serialization edges, transaction-boundary errors are all invisible because the mock returns whatever the test author imagined the real dependency returns. Too broad (real everything including UI and unrelated services): the "integration test" is an e2e test in disguise and pays the e2e cost (slow, flaky, hard to debug) without the focused integration-test cost-benefit ratio. The discipline's central decision is scope — name it explicitly for each test, decide real-vs-faked per dependency on first-principles cost-benefit (is the bug class at this boundary specific to the real dependency? then real; is the real dependency unavailable, costly, or destructive? then faked), choose the test-data lifecycle pattern deliberately (transaction rollback is the default; container reset for the minority where rollback doesn't work). A persistent flake is a bug in the test design — shared mutable state, ordering dependency, time-of-day dependency, race condition — not a property to accept.
Coverage
The discipline of designing tests that verify the interaction between two or more units of a system — modules within a process, services across processes, layers within an architecture, services across organizational boundaries — to catch defects that emerge only at the boundaries. Covers the five primitives (boundary, scope, real-vs-faked-collaborator, test-data lifecycle, pyramid-or-trophy framing), the boundary-type taxonomy (module-to-module, layer-to-layer, service-to-database, service-to-message-bus, service-to-third-party, service-to-service), the test-data lifecycle patterns (full reset, transaction rollback, container reset, shared snapshot), and the pyramid (Cohn 2009) vs trophy (Dodds 2018) framings for how much integration testing the suite should contain. Includes Testcontainers and similar infrastructure as the modern enabler that makes integration testing cheap enough to do well.
Philosophy of the skill
Integration tests verify the parts of a system that no individual unit can verify alone. The bugs they catch — type misalignment, serialization edge cases, transaction boundary errors, configuration mismatches, contract drift, ordering and concurrency issues — live at the boundaries between units. A test suite of comprehensive unit tests and zero integration tests has verified each unit and not the system; the seams are unverified.
The discipline's central design decision is scope: for each test, which collaborators are real (exercised in their integration-bug-finding form) and which are faked (replaced because their realness adds cost without proportional defect-detection). The scope determines the test's identity. Too narrow (mocks at the boundary): the "integration test" is a unit test in disguise and misses the integration bugs. Too broad (real everything, including UI): the "integration test" is an e2e test in disguise and pays the e2e cost.
Modern testing infrastructure — Testcontainers for containerized real dependencies, transaction rollback for fast isolation, parallel execution within and across CI jobs, recorded fakes for third parties — has shifted the cost of integration testing down enough that the test trophy framing (integration-heavy suite) has gained ground on the pyramid (unit-heavy suite). The right ratio for a given codebase depends on which suite costs are real and which are surmountable with infrastructure.
The Pyramid vs The Trophy
Framing
Suite shape
Year
Cost assumption
Best fit
Test Pyramid (Cohn)
Many unit / fewer integration / fewest e2e
2009
Integration tests expensive, slow, flaky
Codebases where integration infra is missing or costly
Test Trophy (Dodds)
Many integration / fewer unit / fewer e2e / static-analysis stem
2018
Integration tests cheap with modern tooling; unit tests pin implementation details
Codebases with strong integration-test infrastructure
Diamond
Many integration / few unit / few e2e
—
Same as trophy minus the static-analysis stem
Codebases where unit tests have lost most value vs the maintenance cost
Both pyramid and trophy agree on: unit tests for fast feedback on implementation logic, integration tests for boundary verification, e2e tests sparingly for full-stack confidence. The disagreement is about the ratio between unit and integration, which depends on what each costs in a given codebase.
Scope Choice — The Defining Decision
For each test, name the scope explicitly. For each dependency in scope, decide real or faked.
Dependency
Real cost
Faked cost
Typical choice
In-process other modules
Free
Loses integration coverage
Real always
Database
Containerized: low (Testcontainers reuse)
In-memory variant: low; loses some real-DB bugs
Real (containerized or in-memory variant)
Message bus
Containerized: low
In-memory variant: loses delivery semantics
Real (containerized) for production-confidence tests
Cache (Redis)
Containerized: low
In-memory fake: loses eviction/TTL bugs
Real (containerized)
Third-party API (paid)
Per-call cost; rate limit
Recorded fake: free, may drift
Recorded fake for PR tests; real sandbox for nightly
Third-party API (free, stable)
Network latency; availability
Recorded fake: free
Real for nightly; recorded for PR
Email / SMS providers
Sends real messages — usually wrong
Capture fake: verifies the call was made
Capture fake; never real in tests
Authentication / OAuth
Real provider often unavailable in test
Issued-token fake
Token fake
The decision rule: use real where the boundary's specific failure modes are integration-bug-finders (database, message bus); use fake where the dependency's realness adds cost (paid APIs) or unacceptable side effects (emails) without proportional defect-detection.
Test Data Lifecycle Patterns
Pattern
Speed
Isolation
When to use
Per-test full reset (drop / recreate)
Slowest (~seconds per test)
Strongest
Tests with destructive schema changes
Per-test transaction rollback
Fast (milliseconds)
Strong (for transactional DBs)
Most database integration tests
Per-suite seed + per-test mutation isolation
Fast
Medium
Read-heavy test suites with limited mutation
Shared snapshot + no-mutation discipline
Fastest
Relies on team discipline
Pure read tests
Container reset per test (Testcontainers)
Medium (container startup)
Strongest cross-process
Tests where transaction rollback isn't viable
The standard production setup is transaction rollback for the bulk of database integration tests, with container reset reserved for the minority where transaction rollback doesn't work (cross-database tests, tests that exercise the transaction system itself).
When To Use Real Dependencies vs Faked
Quick decision table:
Question
If yes
If no
Is the bug class you want to catch at this boundary specific to the real dependency?
Use real
Consider faked
Is the real dependency available in test environment?
Use real or sandbox
Use recorded fake
Is the real dependency's per-test cost acceptable?
Use real
Use recorded fake
Does the real dependency have unacceptable side effects (real emails, real charges)?
Use capture fake
n/a
Does the team have infrastructure for the real dependency (Testcontainers, etc.)?
Use real
Build the infra or use recorded fake
Verification
After applying this skill, verify:
Every integration test's scope is explicit: which collaborators are real, which are faked, what boundary the test exercises. Tests without explicit scope drift between unit and e2e.
Real database, real message bus, real cache are used where their failure modes are integration-bug-finders. Mocking these dependencies usually means the test is unit-scope.
Third-party APIs are faked (recorded responses) for fast PR tests and exercised real in scheduled (nightly/weekly) integration runs.
Test data lifecycle is one of the named patterns (transaction rollback / container reset / per-suite seed / shared no-mutation), not ad-hoc. Test independence is a property of the lifecycle, not a hope.
Flaky integration tests are diagnosed (shared mutable state, ordering dependency, time-of-day dependency, race condition), not accepted. A persistent flake is a bug in the test design.
The pyramid-or-trophy ratio is intentional and reviewed against the codebase's actual integration-test cost and integration-bug rate.
Integration tests are not used where contract tests would be more targeted. The two compose; one does not replace the other.
Integration tests run in CI on every PR (with appropriate scope), not relegated to "nightly only" except for the slowest tier (sandbox third parties, multi-service e2e).
Do NOT Use When
Instead of this skill
Use
Why
Testing a single function in isolation with all collaborators mocked
testing-strategy + test-doubles-design
unit-scope test; this skill is for inter-unit scope
Testing a full user journey through the UI
e2e-test-design
user-journey scope; this skill is for internal seams
Verifying that a service's external interface matches the consumer's expectations
contract-testing
contract scope; this skill verifies implementation through the interface
Measuring whether the test suite catches defects
mutation-testing
quality measurement; this skill is the integration-test design itself
Choosing the overall ratio of test levels
testing-strategy
strategy owns ratios; this skill owns integration-test internals
Snapshot-capturing a complex output
snapshot-testing
snapshot technique; this skill is integration-test scope
Key Sources
Cohn, M. (2009). Succeeding with Agile: Software Development Using Scrum. Addison-Wesley. The book that popularized the test pyramid as the standard recommended suite shape.
Fowler, M. (2012). "The Practical Test Pyramid". The most-cited practitioner essay on the pyramid framing, with practical advice on integration-test scope and infrastructure.
Dodds, K. C. (2018). "The Testing Trophy and Testing Classifications". The essay introducing the test trophy as an alternative to the pyramid, arguing integration tests are the high-value tier.
Testcontainers. "Testcontainers — Reference". The canonical reference for containerized real-dependency integration testing across many languages and dependency types.
Meszaros, G. (2007). xUnit Test Patterns: Refactoring Test Code. Addison-Wesley. Catalog of integration-test patterns including the test-data lifecycle patterns (Setup, Teardown, Shared Fixture, Transaction Rollback).
Fowler, M. "UnitTest" and "IntegrationTest". Reference pages defining the terms practitioners use; both note the hazy line between sociable unit tests and integration tests.
Vocke, H. (2018). "The Practical Test Pyramid — Updated". Updated practitioner guidance on test-pyramid implementation, including integration-test infrastructure recommendations.
Scope: Use when designing tests that verify the interaction between two or more units of a system — modules, services, layers, processes: the scope-and-boundary primitives that distinguish integration from unit and e2e tests, the test-pyramid (Cohn 2009) and test-trophy (Dodds) frameworks for how much integration testing belongs in the suite, the real-vs-faked-collaborator decision per dependency, the test-data lifecycle (per-test setup, transaction rollback, container reset), the difference between sociable-unit tests, integration tests, and contract tests, and the failure modes (over-broad scope that mimics e2e, over-narrow scope that mimics unit, shared mutable state that produces flakes). Do NOT use for testing one unit in isolation (use testing-strategy + test-doubles-design), full user-journey testing (use e2e-test-design), consumer-driven contract verification (use contract-testing), or test-suite quality measurement (use mutation-testing).
When to use
design an integration test for the order service that exercises real database and real message bus
decide which dependencies to fake and which to use real in an integration test
diagnose a flaky integration test — likely shared mutable state across tests
explain why the test pyramid and test trophy disagree on integration test count
Triggers: should this be a unit or integration test, the integration test is flaky, test pyramid vs test trophy, real database in tests, test data setup is taking over
Not for
test a single function in isolation (use testing-strategy + test-doubles-design)
test a full user journey through the UI (use e2e-test-design)
verify a consumer-driven contract against a provider (use contract-testing)
Analogy: An integration test is to a software system what a fire-suppression drill in a specific corridor is to the whole building's safety plan — you are not testing whether each sprinkler head works in isolation (unit), nor whether everyone evacuates the entire building in fifteen minutes (e2e), you are testing whether the smoke detector in this corridor triggers the alarm panel which triggers the sprinkler which actually wets that carpet; the test's identity is the named boundary, and changing the named boundary changes the test's identity.
Common misconception: |
Keywords
integration test, integration testing, test pyramid, test trophy, sociable test, test data setup, test transaction rollback, test containers, testcontainers, boundary test
1---2name: integration-test-design3description: Use when designing tests that verify the interaction between two or more units of a system — modules, services, layers, processes: the scope-and-boundary primitives that distinguish integration from unit and e2e tests, the test-pyramid (Cohn 2009) and test-trophy (Dodds) frameworks for how much integration testing belongs in the suite, the real-vs-faked-collaborator decision per dependency, the test-data lifecycle (per-test setup, transaction rollback, container reset), the difference between sociable-unit tests, integration tests, and contract tests, and the failure modes (over-broad scope that mimics e2e, over-narrow scope that mimics unit, shared mutable state that produces flakes). Do NOT use for testing one unit in isolation (use testing-strategy + test-doubles-design), full user-journey testing (use e2e-test-design), consumer-driven contract verification (use contract-testing), or test-suite quality measurement (use mutation-testing).4license: MIT5---6# Integration-Test Design78## Concept of the skill910Integration test design verifies the interaction between two or more units of a system — modules within a process, services across processes, layers within an architecture — to catch defects that emerge only at the boundaries between those units. The five primitives are: *boundary* (module-to-module, layer-to-layer, service-to-database, service-to-message-bus, service-to-third-party, service-to-service), *scope* (which collaborators are real, which are faked, which are out of scope), *real-vs-faked-collaborator decision per dependency* (real where the boundary's failure modes are integration-bug-finders — database, message bus, cache; faked where the realness adds cost without proportional defect-detection — paid third-party APIs, email/SMS providers), *test-data lifecycle* (full reset, transaction rollback per test, container reset, shared snapshot with no-mutation discipline), and *pyramid-vs-trophy framing* (Cohn 2009: many unit, fewer integration, fewest e2e; Dodds 2018: many integration, fewer unit, fewer e2e, static-analysis stem — integration-heavy when modern tooling makes integration cheap).1112Replaces "comprehensive unit tests covering each unit in isolation" as the sole verification strategy with deliberate seam-verification. Solves the problem that a test suite of comprehensive unit tests has verified each unit but *not the system* — the seams are unverified, and most production failures happen at seams (database transaction boundaries, message-bus delivery semantics, third-party API contract changes, configuration drift between environments). Modern testing infrastructure has shifted integration-test cost down enough that the test trophy framing (integration-heavy suite) has gained ground on the pyramid (unit-heavy suite); the right ratio for any given codebase depends on which suite costs are real (slow tests in CI) and which are surmountable with infrastructure (containerized dependencies, transaction rollback, parallelization).1314Distinct from testing-strategy, which owns the strategic ratio question (how much of each level) — this skill owns the design of integration-level tests specifically. Distinct from test-doubles-design, which owns the construction of mocks/stubs/fakes as constructs — this skill owns the per-dependency real-vs-faked decision in integration scope (integration tests use real where practical, fakes only at true external boundaries; mocking the database in an "integration test" is the most common scope failure). Distinct from e2e-test-design, which owns user-journey-scope tests through the full stack including UI — this skill owns the scope *below* that, interaction of units inside the system, often without UI. Distinct from contract-testing, which owns consumer-driven contract verification between services — contract tests verify the *interface*; integration tests verify the *implementation through* the interface; the two compose, one does not replace the other. Distinct from mutation-testing, which is a test-suite quality measurement applied at any level — this skill is the design of integration-level tests themselves. Distinct from snapshot-testing, which is a capture-and-compare technique applicable inside any test level. An integration test is to a software system what a fire-suppression drill in a specific corridor is to the whole building's safety plan — you are not testing whether each sprinkler head works in isolation (unit), nor whether everyone evacuates the entire building in fifteen minutes (e2e), you are testing whether the smoke detector in *this corridor* triggers the alarm panel which triggers the sprinkler which actually wets *that carpet*; the test's identity is the named boundary, and changing the named boundary changes the test's identity. The wrong mental model is that an integration test is "a unit test with more stuff in it" or "an e2e test with the UI removed." It is neither. Scope failures are the dominant source of fragile integration suites. Too narrow (mocks at the actual boundary): the "integration test" is a unit test in disguise and misses the integration bugs the technique exists to catch — type misalignment, serialization edges, transaction-boundary errors are all invisible because the mock returns whatever the test author imagined the real dependency returns. Too broad (real everything including UI and unrelated services): the "integration test" is an e2e test in disguise and pays the e2e cost (slow, flaky, hard to debug) without the focused integration-test cost-benefit ratio. The discipline's central decision is *scope* — name it explicitly for each test, decide real-vs-faked per dependency on first-principles cost-benefit (is the bug class at this boundary specific to the real dependency? then real; is the real dependency unavailable, costly, or destructive? then faked), choose the test-data lifecycle pattern deliberately (transaction rollback is the default; container reset for the minority where rollback doesn't work). A persistent flake is a bug in the test design — shared mutable state, ordering dependency, time-of-day dependency, race condition — not a property to accept.1516## Coverage1718The discipline of designing tests that verify the interaction between two or more units of a system — modules within a process, services across processes, layers within an architecture, services across organizational boundaries — to catch defects that emerge only at the boundaries. Covers the five primitives (boundary, scope, real-vs-faked-collaborator, test-data lifecycle, pyramid-or-trophy framing), the boundary-type taxonomy (module-to-module, layer-to-layer, service-to-database, service-to-message-bus, service-to-third-party, service-to-service), the test-data lifecycle patterns (full reset, transaction rollback, container reset, shared snapshot), and the pyramid (Cohn 2009) vs trophy (Dodds 2018) framings for how much integration testing the suite should contain. Includes Testcontainers and similar infrastructure as the modern enabler that makes integration testing cheap enough to do well.1920## Philosophy of the skill21Integration tests verify the parts of a system that no individual unit can verify alone. The bugs they catch — type misalignment, serialization edge cases, transaction boundary errors, configuration mismatches, contract drift, ordering and concurrency issues — live at the boundaries between units. A test suite of comprehensive unit tests and zero integration tests has verified each unit and not the system; the seams are unverified.2223The discipline's central design decision is *scope*: for each test, which collaborators are real (exercised in their integration-bug-finding form) and which are faked (replaced because their realness adds cost without proportional defect-detection). The scope determines the test's identity. Too narrow (mocks at the boundary): the "integration test" is a unit test in disguise and misses the integration bugs. Too broad (real everything, including UI): the "integration test" is an e2e test in disguise and pays the e2e cost.2425Modern testing infrastructure — Testcontainers for containerized real dependencies, transaction rollback for fast isolation, parallel execution within and across CI jobs, recorded fakes for third parties — has shifted the cost of integration testing down enough that the test trophy framing (integration-heavy suite) has gained ground on the pyramid (unit-heavy suite). The right ratio for a given codebase depends on which suite costs are real and which are surmountable with infrastructure.2627## The Pyramid vs The Trophy2829| Framing | Suite shape | Year | Cost assumption | Best fit |30|---|---|---|---|---|31| Test Pyramid (Cohn) | Many unit / fewer integration / fewest e2e | 2009 | Integration tests expensive, slow, flaky | Codebases where integration infra is missing or costly |32| Test Trophy (Dodds) | Many integration / fewer unit / fewer e2e / static-analysis stem | 2018 | Integration tests cheap with modern tooling; unit tests pin implementation details | Codebases with strong integration-test infrastructure |33| Diamond | Many integration / few unit / few e2e | — | Same as trophy minus the static-analysis stem | Codebases where unit tests have lost most value vs the maintenance cost |3435Both pyramid and trophy agree on: unit tests for fast feedback on implementation logic, integration tests for boundary verification, e2e tests sparingly for full-stack confidence. The disagreement is about the ratio between unit and integration, which depends on what each costs in a given codebase.3637## Scope Choice — The Defining Decision3839For each test, name the scope explicitly. For each dependency in scope, decide real or faked.4041| Dependency | Real cost | Faked cost | Typical choice |42|---|---|---|---|43| In-process other modules | Free | Loses integration coverage | Real always |44| Database | Containerized: low (Testcontainers reuse) | In-memory variant: low; loses some real-DB bugs | Real (containerized or in-memory variant) |45| Message bus | Containerized: low | In-memory variant: loses delivery semantics | Real (containerized) for production-confidence tests |46| Cache (Redis) | Containerized: low | In-memory fake: loses eviction/TTL bugs | Real (containerized) |47| Third-party API (paid) | Per-call cost; rate limit | Recorded fake: free, may drift | Recorded fake for PR tests; real sandbox for nightly |48| Third-party API (free, stable) | Network latency; availability | Recorded fake: free | Real for nightly; recorded for PR |49| Email / SMS providers | Sends real messages — usually wrong | Capture fake: verifies the call was made | Capture fake; never real in tests |50| Authentication / OAuth | Real provider often unavailable in test | Issued-token fake | Token fake |5152The decision rule: use real where the boundary's specific failure modes are integration-bug-finders (database, message bus); use fake where the dependency's realness adds cost (paid APIs) or unacceptable side effects (emails) without proportional defect-detection.5354## Test Data Lifecycle Patterns5556| Pattern | Speed | Isolation | When to use |57|---|---|---|---|58| Per-test full reset (drop / recreate) | Slowest (~seconds per test) | Strongest | Tests with destructive schema changes |59| Per-test transaction rollback | Fast (milliseconds) | Strong (for transactional DBs) | Most database integration tests |60| Per-suite seed + per-test mutation isolation | Fast | Medium | Read-heavy test suites with limited mutation |61| Shared snapshot + no-mutation discipline | Fastest | Relies on team discipline | Pure read tests |62| Container reset per test (Testcontainers) | Medium (container startup) | Strongest cross-process | Tests where transaction rollback isn't viable |6364The standard production setup is transaction rollback for the bulk of database integration tests, with container reset reserved for the minority where transaction rollback doesn't work (cross-database tests, tests that exercise the transaction system itself).6566## When To Use Real Dependencies vs Faked6768Quick decision table:6970| Question | If yes | If no |71|---|---|---|72| Is the bug class you want to catch at this boundary specific to the real dependency? | Use real | Consider faked |73| Is the real dependency available in test environment? | Use real or sandbox | Use recorded fake |74| Is the real dependency's per-test cost acceptable? | Use real | Use recorded fake |75| Does the real dependency have unacceptable side effects (real emails, real charges)? | Use capture fake | n/a |76| Does the team have infrastructure for the real dependency (Testcontainers, etc.)? | Use real | Build the infra or use recorded fake |7778## Verification7980After applying this skill, verify:81- [ ] Every integration test's scope is explicit: which collaborators are real, which are faked, what boundary the test exercises. Tests without explicit scope drift between unit and e2e.82- [ ] Real database, real message bus, real cache are used where their failure modes are integration-bug-finders. Mocking these dependencies usually means the test is unit-scope.83- [ ] Third-party APIs are faked (recorded responses) for fast PR tests and exercised real in scheduled (nightly/weekly) integration runs.84- [ ] Test data lifecycle is one of the named patterns (transaction rollback / container reset / per-suite seed / shared no-mutation), not ad-hoc. Test independence is a property of the lifecycle, not a hope.85- [ ] Flaky integration tests are diagnosed (shared mutable state, ordering dependency, time-of-day dependency, race condition), not accepted. A persistent flake is a bug in the test design.86- [ ] The pyramid-or-trophy ratio is intentional and reviewed against the codebase's actual integration-test cost and integration-bug rate.87- [ ] Integration tests are not used where contract tests would be more targeted. The two compose; one does not replace the other.88- [ ] Integration tests run in CI on every PR (with appropriate scope), not relegated to "nightly only" except for the slowest tier (sandbox third parties, multi-service e2e).8990## Do NOT Use When9192| Instead of this skill | Use | Why |93|---|---|---|94| Testing a single function in isolation with all collaborators mocked | `testing-strategy` + `test-doubles-design` | unit-scope test; this skill is for inter-unit scope |95| Testing a full user journey through the UI | `e2e-test-design` | user-journey scope; this skill is for internal seams |96| Verifying that a service's external interface matches the consumer's expectations | `contract-testing` | contract scope; this skill verifies implementation through the interface |97| Measuring whether the test suite catches defects | `mutation-testing` | quality measurement; this skill is the integration-test design itself |98| Choosing the overall ratio of test levels | `testing-strategy` | strategy owns ratios; this skill owns integration-test internals |99| Snapshot-capturing a complex output | `snapshot-testing` | snapshot technique; this skill is integration-test scope |100101## Key Sources102103- Cohn, M. (2009). *Succeeding with Agile: Software Development Using Scrum*. Addison-Wesley. The book that popularized the test pyramid as the standard recommended suite shape.104- Fowler, M. (2012). ["The Practical Test Pyramid"](https://martinfowler.com/articles/practical-test-pyramid.html). The most-cited practitioner essay on the pyramid framing, with practical advice on integration-test scope and infrastructure.105- Dodds, K. C. (2018). ["The Testing Trophy and Testing Classifications"](https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications). The essay introducing the test trophy as an alternative to the pyramid, arguing integration tests are the high-value tier.106- Testcontainers. ["Testcontainers — Reference"](https://testcontainers.com/). The canonical reference for containerized real-dependency integration testing across many languages and dependency types.107- Meszaros, G. (2007). *xUnit Test Patterns: Refactoring Test Code*. Addison-Wesley. Catalog of integration-test patterns including the test-data lifecycle patterns (Setup, Teardown, Shared Fixture, Transaction Rollback).108- Fowler, M. ["UnitTest"](https://martinfowler.com/bliki/UnitTest.html) and ["IntegrationTest"](https://martinfowler.com/bliki/IntegrationTest.html). Reference pages defining the terms practitioners use; both note the hazy line between sociable unit tests and integration tests.109- Vocke, H. (2018). ["The Practical Test Pyramid — Updated"](https://martinfowler.com/articles/practical-test-pyramid.html). Updated practitioner guidance on test-pyramid implementation, including integration-test infrastructure recommendations.110- ThoughtWorks. ["Test Doubles" and "Test pyramid" in the Technology Radar](https://www.thoughtworks.com/radar). Industry-practitioner consensus on integration-test patterns evolving over years.111112## Skill Graph context113114<!-- skill-graph-context:start (generated — do not edit by hand) -->115116**Classification**117- Subject: `quality-assurance`118- Public: `true`119- Domain: `quality/testing`120- Scope: Use when designing tests that verify the interaction between two or more units of a system — modules, services, layers, processes: the scope-and-boundary primitives that distinguish integration from unit and e2e tests, the test-pyramid (Cohn 2009) and test-trophy (Dodds) frameworks for how much integration testing belongs in the suite, the real-vs-faked-collaborator decision per dependency, the test-data lifecycle (per-test setup, transaction rollback, container reset), the difference between sociable-unit tests, integration tests, and contract tests, and the failure modes (over-broad scope that mimics e2e, over-narrow scope that mimics unit, shared mutable state that produces flakes). Do NOT use for testing one unit in isolation (use testing-strategy + test-doubles-design), full user-journey testing (use e2e-test-design), consumer-driven contract verification (use contract-testing), or test-suite quality measurement (use mutation-testing).121122**When to use**123- design an integration test for the order service that exercises real database and real message bus124- decide which dependencies to fake and which to use real in an integration test125- diagnose a flaky integration test — likely shared mutable state across tests126- explain why the test pyramid and test trophy disagree on integration test count127- Triggers: `should this be a unit or integration test`, `the integration test is flaky`, `test pyramid vs test trophy`, `real database in tests`, `test data setup is taking over`128129**Not for**130- test a single function in isolation (use testing-strategy + test-doubles-design)131- test a full user journey through the UI (use e2e-test-design)132- verify a consumer-driven contract against a provider (use contract-testing)133134**Related skills**135- Verify with: `testing-strategy`, `e2e-test-design`136- Related: `test-driven-development`, `testing-strategy`, `test-doubles-design`, `e2e-test-design`, `contract-testing`137138**Concept**139- Mental model: |140- Purpose: |141- Boundary: |142- Analogy: An integration test is to a software system what a fire-suppression drill in a specific corridor is to the whole building's safety plan — you are not testing whether each sprinkler head works in isolation (unit), nor whether everyone evacuates the entire building in fifteen minutes (e2e), you are testing whether the smoke detector in *this corridor* triggers the alarm panel which triggers the sprinkler which actually wets *that carpet*; the test's identity is the named boundary, and changing the named boundary changes the test's identity.143- Common misconception: |144145**Keywords**146- `integration test`, `integration testing`, `test pyramid`, `test trophy`, `sociable test`, `test data setup`, `test transaction rollback`, `test containers`, `testcontainers`, `boundary test`147148<!-- skill-graph-context:end -->
Run npx skillmds@latest add jacob-balslev/integration-test-design in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Use when designing tests that verify the interaction between two or more units of a system — modules, services, layers, processes: the scope-and-boundary primitives that distinguish integration from unit and e2e tests, the test-pyramid (Cohn 2009) and test-trophy (Dodds) frameworks for how much integration testing belongs in the suite, the real-vs-faked-collaborator decision per dependency, the test-data lifecycle (per-test setup, transaction rollback, container reset), the difference between sociable-unit tests, integration tests, and contract tests, and the failure modes (over-broad scope that mimics e2e, over-narrow scope that mimics unit, shared mutable state that produces flakes). Do NOT use for testing one unit in isolation (use testing-strategy + test-doubles-design), full user-journey testing (use e2e-test-design), consumer-driven contract verification (use contract-testing), or test-suite quality measurement (use mutation-testing). It is listed under Integrations & APIs on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: makes network calls, reads secrets. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
jacob-balslev (@jacob-balslev) published this skill. Their other Agent Skills are listed on their SkillMD profile.