Testing with Jest
Jest is a delightful JavaScript Testing Framework with a focus on simplicity. Created and maintained primarily by Facebook, it is widely used in the React ecosystem, often paired with React Testing Library. It provides a complete and easy-to-use testing solution out of the box.
1. Test Setup and Basics
In most modern React setups (like Create React App or Next.js), Jest is pre-configured. To run tests, you typically use npm test or yarn test.
The Global Functions: describe, it, and expect
Jest provides global functions to structure and assert your tests.
describe(name, fn): Groups related tests together into a test suite.it(name, fn)ortest(name, fn): Defines a single test case.expect(value): Used to make assertions about the code's behavior.
Basic Example:
const sum = (a, b) => a + b;
describe('Math functions', () => {
it('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
});
Common Matchers
Matchers are methods chained to expect() to check values.
.toBe(value): Exact equality (usesObject.is)..toEqual(value): Recursive checks every field of an object or array (deep equality)..toBeTruthy()/.toBeFalsy(): Matches anything that anifstatement treats as true/false..toContain(item): Checks if an array or string contains a specific item..toThrow(error): Checks if a function throws an error.
2. Testing Asynchronous Code
Testing asynchronous code requires telling Jest when the asynchronous work is complete so it doesn't finish the test prematurely.
Using Promises
Return a promise from your test, and Jest will wait for it to resolve.
it('fetches user data', () => {
return fetchUser(1).then(data => {
expect(data.name).toBe('John Doe');
});
});
Using Async/Await (Recommended)
The cleanest way to test async code is using standard async/await syntax.
it('fetches user data using async/await', async () => {
const data = await fetchUser(1);
expect(data.name).toBe('John Doe');
});
3. Mocking in Jest
Mocking allows you to replace real implementations with fake ones to isolate the code you are testing. This is crucial when testing components that rely on APIs or complex external libraries.
Mock Functions (jest.fn())
A mock function lets you track how it is called and control what it returns.
it('tracks calls and returns a mocked value', () => {
const mockCallback = jest.fn(x => 42 + x);
mockCallback(0);
mockCallback(1);
// The mock function was called twice
expect(mockCallback.mock.calls.length).toBe(2);
// The first argument of the first call was 0
expect(mockCallback.mock.calls[0][0]).toBe(0);
});
Mocking Modules (jest.mock())
You can mock entire modules to avoid hitting real APIs during tests.
import axios from 'axios';
import { fetchGreeting } from './greeting';
jest.mock('axios');
it('fetches a greeting successfully', async () => {
axios.get.mockResolvedValue({ data: { message: 'Hello World' } });
const greeting = await fetchGreeting();
expect(greeting).toBe('Hello World');
expect(axios.get).toHaveBeenCalledWith('/api/greeting');
});
4. Snapshot Testing
Snapshot testing is a useful tool when you want to ensure your UI does not change unexpectedly.
When you run a snapshot test for the first time, Jest renders the component, captures its output, and saves it in a .snap file alongside your test. On subsequent test runs, Jest compares the new output to the saved snapshot.
import React from 'react';
import renderer from 'react-test-renderer';
import MyButton from './MyButton';
it('renders correctly', () => {
const tree = renderer
.create(<MyButton label="Click Me" />)
.toJSON();
expect(tree).toMatchSnapshot();
});
- If the snapshot matches: The test passes.
- If the snapshot differs: The test fails. If the change was intentional (e.g., you updated the button's design), you can update the snapshot by running Jest with the
-uflag (npm test -- -u).
Summary
- Use
describeanditto structure tests, andexpectwith matchers to assert correctness. - Handle async tests using
async/await. - Use
jest.fn()andjest.mock()to isolate your code from external dependencies like APIs. - Utilize Snapshot testing to quickly catch unintended UI regressions.