How to Test Code Splitting in React
Testing code splitting in React ensures that dynamically imported
components load correctly and that your application handles loading
states gracefully. This article provides a straightforward guide on how
to test React components configured with React.lazy and
Suspense using Jest and React Testing Library. You will
learn how to verify loading fallbacks, test the rendered outcome of
asynchronous chunks, and handle mocked dynamic imports.
Understanding the Testing Challenge
Code splitting relies on dynamic import() statements,
which are asynchronous by nature. When a component is loaded lazily
using React.lazy(), React pauses rendering of that
component and displays a Suspense fallback (like a spinner)
until the chunk is fetched and resolved. To test this behavior, your
tests must handle this asynchronous transition.
1. Testing the Suspense Fallback State
To verify that your application displays a loading indicator while the split code is being fetched, you need to test the initial render before the promise resolves.
import React, { Suspense } from 'react';
import { render, screen } from '@testing-library/react';
const LazyComponent = React.lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
test('should render the loading fallback initially', () => {
render(<App />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});2. Testing the Loaded Component (Asynchronous Resolution)
To test the final rendered component after the dynamic import has
successfully loaded, use React Testing Library’s asynchronous queries.
The findBy* queries are designed for this scenario because
they repeatedly query the DOM until the element appears or the timeout
is reached.
import React, { Suspense } from 'react';
import { render, screen } from '@testing-library/react';
const LazyComponent = React.lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
test('should render the lazy component after loading', async () => {
render(<App />);
// Wait for the fallback to disappear and the real component to render
const lazilyLoadedElement = await screen.findByText('Hello from Lazy Component');
expect(lazilyLoadedElement).toBeInTheDocument();
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});3. Mocking Lazy Components for Unit Tests
If you want to isolate your tests and avoid loading actual network chunks during unit testing, you can mock the dynamic import using Jest. This is useful when you want to focus on testing the parent component’s logic without waiting for real asynchronous resolutions.
import React from 'react';
import { render, screen } from '@testing-library/react';
// Mock the component that is lazily loaded
jest.mock('./MyComponent', () => {
return function DummyComponent() {
return <div data-testid="mocked-lazy">Mocked Component</div>;
};
});
test('renders mocked component instantly', () => {
render(<App />);
expect(screen.getByTestId('mocked-lazy')).toBeInTheDocument();
});By combining fallback state assertions, asynchronous queries like
findBy, and module mocking, you can thoroughly test the
behavior and robustness of code-split applications in React.