Testing JavaScript
Integration Testing Concepts
Master Integration Testing in JavaScript. Learn to test how multiple components, state modules, and routing endpoints interact.
1. Introduction
Integration Testing verifies that different modules, classes, or database services interact correctly with each other. While unit tests focus on isolated blocks of code, integration tests focus on the integration boundaries between them.
2. Why It Matters
An application can fail even if all individual units pass their tests. For example, a validation helper may format values correctly, and a database connector may save objects correctly, but if the save method passes arguments in the wrong order, the application will crash. Integration testing catches these integration failures.
3. Real-World Analogy
Think of a Plumbing system:
- Unit Test (Pipe Check): The factory tests each pipe to ensure it holds pressure and doesn't leak. Every pipe passes inspection.
- Integration Test (Connected Pipes): You connect the pipes to a water tap. Even if all pipes are perfect, if the threads do not match or the joints are loose, water will leak at the connection point. Testing the flow of water through the connected system is an integration test.
4. Integration Test Flow
Integration tests focus on testing a subsystem of components without mocking the connections between them:
Instead of mocking the database layer (unit test), an integration test runs both components, verifying that the validated payload is correctly written to a database:
5. DOM Testing Integration
In frontend applications, integration testing often involves rendering components in a simulated DOM (using jsdom) and asserting that DOM updates trigger state updates correctly:
6. Common Mistakes
- Over-mocking in integration tests: Mocking internal layers inside an integration test defeats its purpose. If you replace the database layer with a mock, the test becomes a unit test, failing to verify that database triggers function correctly. Mock only external, network-boundary resources.
7. Quick Quiz
Q1: What is the primary difference between a unit test and an integration test?
A) Unit tests run in browsers while integration tests run in terminal outputs
B) Unit tests check isolated functions, while integration tests verify the interaction between multiple modules or components
Answer: B — Integration tests verify communication boundaries and interactions between multiple components.
8. Scenario-Based Challenge
The Multi-Step Checkout Flow Integration:
A checkout flow involves a cart manager, a discount code validator, and a shipping calculator. Design an integration test that simulates adding an item, applying a coupon, and calculating the final shipping costs, verifying the final total matches without mocking these internal calculators.
9. Debugging Exercise
Explain why this test database cleanup strategy fails during concurrent test execution, and how to fix it:
// Integration test file import { db } from './db-connection';beforeEach(async () => { // Bug: clearing the shared dev database tables before every test! await db.query('DELETE FROM users'); });
test('adds a user', async () => { await db.addUser({ id: 1, name: 'Alice' }); const list = await db.getUsers(); expect(list.length).toBe(1); }); // When running tests concurrently, sibling tests delete tables during execution, causing random failures! Why?
View Solution
Diagnosis: The tests share a single, shared developer database connection. When tests run in parallel, one test's beforeEach block clears tables while another test is running, causing random failures due to shared state pollution.
Fix: Create an isolated database session (such as using separate in-memory SQLite tables, random database schemas, or database transaction rollbacks) for every worker thread:
// Use transactions and roll back after each test beforeEach(async () => { await db.query('BEGIN TRANSACTION'); });
afterEach(async () => { await db.query('ROLLBACK'); // Restores database state instantly, leaving tables untouched! });
10. Interview Questions
🟢 Q1: Why is integration testing important even if unit test coverage is 100%?
Answer: Unit tests check code blocks in isolation. Even if every function works perfectly by itself, errors can occur when they interact.
These errors include:
• API Mismatches: Passing arguments in the wrong format (e.g. string instead of object) to a downstream module.
• Side Effects: Database schema constraints rejecting queries that unit tests (which used mocked DB wrappers) failed to check.
• Asynchronous Race Conditions: Two components updating a shared state variable concurrently, resulting in race conditions.
11. Production Considerations
- • Isolated Environments: Run integration tests in containerized environments (such as using Docker) that spawn isolated databases and services, ensuring tests do not contaminate local development data.