Introduction to Unit Testing with Jest and React Testing Library
This article explains the concept of unit testing, introduces the Jest framework and its configuration, demonstrates common APIs, matchers, asynchronous testing, mocking, coverage reporting, and shows how to test React components using React Testing Library with practical code examples.
Preface
Unit testing is a testing practice used to verify the correctness of a module, function, or class.
Unit Testing
Unit testing validates that a given input produces a deterministic output. It is most valuable for complex, error‑prone, core, or shared code. Test cases are usually written alongside implementation to aid understanding of code and requirements.
Jest
Jest is an open‑source front‑end testing framework from Facebook, primarily used for React unit tests and bundled with Create React App. It runs tests in an isolated environment using jsdom, and provides mocking, asynchronous testing, coverage, snapshot testing, and more.
Jest Basics
Installation and Configuration
Install via npm or yarn:
npm i --save-dev jest
yarn add -dev jestAdd a test script to package.json :
{
"scripts": {
"test": "jest"
}
}Jest can be configured via a jest field in package.json or with a separate config file. Running npm test will execute files ending with .test.js or located in __tests__ .
Global APIs
Common global APIs provided by Jest:
describe(name, fn) – groups related test cases.
test(name, fn, timeout) (alias it ) – defines an individual test.
expect(value) – creates an assertion.
afterAll(fn, timeout) – runs after all tests.
afterEach(fn, timeout) – runs after each test.
beforeAll(fn, timeout) – runs before all tests.
beforeEach(fn, timeout) – runs before each test.
The hook functions can be scoped to a describe block, where their execution order differs from the global scope.
beforeAll(() => { console.log('outer beforeAll1'); });
beforeEach(() => { console.log('outer beforeEach1'); });
afterAll(() => { console.log('outer afterAll1'); });
describe('my test', () => {
beforeAll(() => { console.log('inner beforeAll2'); });
beforeEach(() => { console.log('inner beforeEach2'); });
afterAll(() => { console.log('inner afterAll2'); });
test('must Equal', () => {
expect('must be strictly equal').toBe('must be strictly equal');
});
test('test NaN', () => {
expect(NaN).toBe(NaN);
});
});
// console output order:
// outer beforeAll1
// inner beforeAll2
// outer beforeEach1
// inner beforeEach2
// outer beforeEach1
// inner beforeEach2
// inner afterAll2
// outer afterAll1Common Matchers
toBe(value) – strict equality (uses Object.is ).
toEqual(value) – deep equality.
toBeNull() , toBeUndefined() , toBeDefined() .
toMatch(regexp|string) – regex or string match.
toBeTruthy() , toBeFalsy() .
toContain(item) – array contains.
toBeGreaterThan/…OrEqual , toBeLessThan/…OrEqual .
not – negates a matcher.
resolves , rejects – handle promises.
assertions(number) – expect a specific number of assertions.
Matchers are used after expect(value) to form an assertion; failures are reported in the test output.
describe('test', () => {
test('toContain', () => {
const arr = [1, 2, 3];
expect(arr).toContain(1); // passes
});
test('toMatch', () => {
const str = 'Hello world!';
expect(str).toMatch(/hello world!/i); // passes
});
});Testing Asynchronous Code
Asynchronous code can be tested via callbacks or promises.
// Callback example
export const getCallbackData = fn => {
setTimeout(() => {
fn({ data: 'callback' });
}, 1000);
};
// Promise that resolves
export const getPromiseResolve = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ data: 'resolve' });
}, 1000);
});
};
// Promise that rejects
export const getPromiseReject = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject({ data: 'reject' });
}, 1000);
});
};Test cases for the above:
import { getCallbackData, getPromiseResolve, getPromiseReject } from './async';
describe('test', () => {
test('callback without done (fails)', () => {
getCallbackData(res => {
expect(res).toEqual({ data: 'callback' });
});
});
test('callback with done', done => {
getCallbackData(res => {
expect(res).toEqual({ data: 'callback' });
done();
});
});
test('promise resolve with return', () => {
return getPromiseResolve().then(res => {
expect(res.data).toBe('resolve');
});
});
test('promise resolve with async/await', async () => {
const res = await getPromiseResolve();
expect(res).toEqual({ data: 'resolve' });
});
test('promise reject with rejects matcher', () => {
return expect(getPromiseReject()).rejects.toEqual({ data: 'reject' });
});
test('promise reject with catch and assertions', () => {
expect.assertions(1);
return getPromiseReject().catch(err => {
expect(err.data).toEqual('reject');
});
});
});Mock System
Jest provides powerful mocking utilities:
jest.fn(implementation?) – creates a mock function.
jest.mock(moduleName, factory?, options?) – mocks a module.
jest.spyOn(object, methodName) – spies on an existing method.
import axios from "axios";
jest.mock("axios"); // mock the axios module
describe('test', () => {
test('jest.fn', () => {
const mockFn = jest.fn(() => 'jest.fn');
const res = mockFn();
expect(mockFn).toBeCalled();
expect(res).toBe('jest.fn');
mockFn.mockImplementation(() => 'mockImplementation');
expect(mockFn()).toBe('mockImplementation');
});
test('jest.mock', () => {
axios.get.mockResolvedValueOnce({ data: 'jest.mock' });
return axios.get('xxx.json').then(res => {
expect(res).toEqual({ data: 'jest.mock' });
});
});
});Test Coverage
Coverage measures how much of the code is exercised by tests. Jest integrates Istanbul to generate coverage reports via npm test -- --coverage or a dedicated script.
"scripts": {
"test": "jest",
"coverage": "jest --coverage"
}Coverage reports include statement, branch, function, and line coverage. Branch coverage is especially important, but true thoroughness requires testing each logical condition.
Testing React Components
React Testing Library (RTL) is the recommended tool for testing React components. It is bundled with Create React App; otherwise install with npm install --save-dev @testing-library/react . Enzyme is an alternative.
render Method
RTL’s render returns utilities such as query functions, a container , and a cleanup function.
Query Utilities
Common queries include getByText , queryByText , findByText , and getByTestId . getBy* throws on failure, queryBy* returns null , and findBy* is async.
Firing Events
Use fireEvent[eventName](node, eventProperties?) to simulate user interactions.
jest-dom Matchers
jest-dom extends Jest with DOM‑specific matchers such as toBeVisible() , toHaveClass() , toBeEnabled() , and toBeDisabled() .
Example
// Component that disables a button temporarily
const FrozeButton = (props) => {
const { onClick = () => {}, delay = 1000, children } = props;
const [disabled, setDisabled] = useState(false);
const timerRef = useRef(null);
const frozeFn = () => {
setDisabled(true);
timerRef.current = setTimeout(() => setDisabled(false), delay);
};
useEffect(() => () => clearTimeout(timerRef.current), []);
return (
{ onClick(); frozeFn(); }} disabled={disabled}>
{children}
);
};Test case for the component:
import { render, waitFor, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
describe('test', () => {
test('frozeButton', async () => {
const onClick = jest.fn();
const props = { onClick, delay: 1200 };
const { getByText } = render(
clickToFroze
);
const btn = getByText('clickToFroze');
expect(btn).toBeEnabled();
fireEvent.click(btn);
expect(onClick).toBeCalled();
expect(btn).toBeDisabled();
await waitFor(() => expect(btn).toBeEnabled(), { timeout: 1500 });
expect(btn).toBeEnabled();
});
});Conclusion
React Testing Library is powerful; this article covered its main features and a simple example. Combined with Jest, it can also test component lifecycles, Redux integration, custom hooks, and more.
Summary
The article introduced unit testing concepts, basic usage of Jest, and how to test React components with React Testing Library. While unit testing adds short‑term effort, it improves code quality and reduces bugs in the long run, giving developers confidence in their code.
References
Jest official site: https://jestjs.io/zh-Hans/
React Testing Library: https://testing-library.com/docs/react-testing-library/intro/
FreeCodeCamp guide: https://www.freecodecamp.org/news/8-simple-steps-to-start-testing-react-apps-using-react-testing-library-and-jest/
New Oriental Technology
Practical internet development experience, tech sharing, knowledge consolidation, and forward-thinking insights.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.