Unit Testing React Components with API Calls Using Jest
When building React components that involve fetching data from external APIs, it’s crucial to ensure that the components behave as expected under various conditions. This involves testing not only the component’s rendering logic but also its ability to handle data fetching, loading states, and error handling. In this blog post, we’ll explore how to unit test a React component named Posts
, which fetches and displays posts from an external API. We will also discuss how to use Jest’s mocking features to simulate API calls for a robust testing environment.
The Posts Component
The Posts
component fetches a list of posts from the JSONPlaceholder API and displays them. Here’s a quick rundown of its functionality:
- It initializes with an empty list of posts.
- It fetches the posts from the API on component mount.
- While the data is loading, it displays a “Loading…” message.
- Once data is fetched, it renders the posts.
Here’s the component code:
.
import React, { useState, useEffect } from 'react';
function Posts() {
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => setPosts(data))
.catch(error => console.error('Error fetching data: ', error));
}, []);
if (posts.length === 0) {
return Loading...;
}
return (
{posts.map(post => (
{post.title}
{post.body}
))}
);
}
export default Posts;
Testing Strategy
The testing strategy for the Posts
component includes:
- Mocking Fetch API: Since the component relies on external API calls, we will mock these calls to control the test environment and avoid making actual HTTP requests.
- Testing Loading State: Verify that the component correctly displays the loading state.
- Testing Successful Data Fetch: Ensure that the component correctly renders the fetched data.
- Error Handling: Optionally, you could also test how the component handles a failed API call (not covered here, but recommended).
Using Jest to Mock Fetch API
Jest.fn() and Mocking Modules
jest.fn()
is a function that returns a new, unused mock function. Alternatively, you can use jest.spyOn()
to spy or mock functions or methods.
Mocking Fetch
Here is how you can mock the fetch
function globally using Jest:
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(fakePosts)
})
);
Writing Tests
Now, let’s write some tests using React Testing Library alongside our Jest mocks:
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import Posts from './Posts';
const fakePosts = [
{ id: 1, title: 'Post 1', body: 'This is post 1' },
{ id: 2, title: 'Post 2', body: 'This is post 2' }
];
beforeEach(() => {
fetch.mockClear();
fetch.mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(fakePosts)
})
);
});
test('displays loading message initially', () => {
render( );
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
test('displays posts after fetch', async () => {
render( );
await waitFor(() => {
expect(screen.getByText('Post 1')).toBeInTheDocument();
expect(screen.getByText('This is post 1')).toBeInTheDocument();
expect(screen.getByText('Post 2')).toBeInTheDocument();
expect(screen.getByText('This is post 2')).toBeInTheDocument();
});
});
Mocking API calls using Jest and testing asynchronous behavior are critical parts of ensuring that React components with external dependencies behave correctly. By using `jest.fn()`
to mock functions and controlling the fetch API responses, you can simulate various scenarios and effectively test your React components.