The difference between unit tests and integration tests, when to use each, the testing pyramid versus testing trophy, and how integration tests provide the best return on investment for most applications.
Verify single units in isolation with mocked dependencies. Fast and focused, but can be fragile when tightly coupled to implementation details.
Test multiple units working together with minimal mocking. More realistic and resilient to refactoring, catching bugs at component boundaries where most issues occur.
The pyramid favors many unit tests at the base. The modern trophy approach puts integration tests as the largest layer, arguing they provide the best return on investment.
Mock Service Worker intercepts network requests at the service worker level, enabling realistic integration tests that work with any HTTP client.
Understanding the distinction between unit tests and integration tests -- and knowing when to use each -- is a fundamental testing interview topic. The industry has shifted from dogmatic adherence to the testing pyramid toward more nuanced approaches like the testing trophy, making this a rich area for discussion.
Unit tests verify a single function, method, or module in complete isolation. All external dependencies (other modules, APIs, databases) are mocked or stubbed. Characteristics:
Best for: pure functions, business logic calculations, utility functions, state machines, and algorithmic code.
Integration tests verify that multiple units work together correctly. They exercise real interactions between components, modules, or services with minimal mocking. Characteristics:
Best for: React component rendering with user interactions, API route handlers with middleware, form submissions with validation, and data flow between modules.
Mike Cohn's testing pyramid (2009) recommends many unit tests at the base, fewer integration tests in the middle, and very few E2E tests at the top. The rationale: unit tests are fast and cheap, so build confidence from the bottom up.
Kent C. Dodds proposed the testing trophy as a modern alternative, arguing that integration tests provide the highest return on investment. The trophy shape (wider in the middle) reflects:
The reasoning: integration tests catch real bugs at component boundaries where most issues occur, without the brittleness of over-mocked unit tests or the slowness of E2E tests.
Consider a shopping cart component. A unit test might verify the calculateTotal function with mocked inputs. An integration test would render the entire cart component, simulate adding items, and verify the displayed total updates correctly. The integration test catches rendering bugs, state management issues, and event handler problems that the unit test misses.
Mock Service Worker (MSW) intercepts network requests at the service worker level, making it ideal for integration tests. Unlike mocking fetch directly, MSW works with any HTTP client and tests the full request/response cycle:
const server = setupServer(
http.get('/api/users', () => HttpResponse.json([{ name: 'Alice' }]))
);This approach keeps tests realistic while controlling external dependencies.
Fun Fact
The original testing pyramid from Mike Cohn's 2009 book 'Succeeding with Agile' did not include static analysis at all. Kent C. Dodds added it to the testing trophy because TypeScript and ESLint now catch entire categories of bugs that previously required manual tests.