Integration testing checks how different parts of your app work together.
Instead of testing a single function or isolated component, you test multiple units interacting — for example, a component + its child components + API calls + state updates.
Example scenarios:
- A form component interacting with a validation hook and a submit handler.
- A parent component passing props to multiple children and updating state based on events.
- A data-fetching component rendering a loader, showing fetched data, and handling errors.
- Catches issues between modules/components (gaps unit tests miss).
- Verifies that data flows correctly between parts.
- Ensures UI and business logic work together as expected.
| Type | Scope | Goal | Speed |
|---|---|---|---|
| Unit Testing | Smallest unit (function/component) | Test internal logic in isolation | Fast |
| Integration Testing | Multiple units/modules working together | Test their interaction | Medium |
| End-to-End (E2E) | Whole app in a real browser | Test full user flows | Slow |
Here we test:
- Parent component → fetches data
- Child component → displays items
- API interaction → mocked
// UserList.js
import { useEffect, useState } from 'react';
function User({ name }) {
return <li>{name}</li>;
}
export default function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then((res) => res.json())
.then((data) => {
setUsers(data);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
return (
<ul>
{users.map((user) => (
<User key={user} name={user} />
))}
</ul>
);
}Test:
// UserList.test.js
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
beforeEach(() => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(['Alice', 'Bob']),
})
);
});
test('loads and displays users', async () => {
render(<UserList />);
// Initial state
expect(screen.getByText(/loading/i)).toBeInTheDocument();
// Wait for users to load
await waitFor(() => {
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
});- Mock APIs → Use Jest mocks or MSW to isolate tests from real network calls.
- Focus on interactions & data flow → Not on implementation details.
- Avoid overly large scope → Don’t turn integration tests into full E2E tests.
- Use
waitFororfindBy→ For async state changes. - Test critical paths → Data fetching, form submission, authentication.
▲
| End-to-End Tests (few)
| Integration Tests (moderate)
| Unit Tests (many)
▼
- Unit tests → Verify building blocks
- Integration tests → Verify blocks fit together
- E2E → Verify entire house works for the user