react 如何做单元测试
单元测试的基本概念
单元测试是软件开发中的一种测试方法,用于验证代码中最小可测试单元的正确性。在 React 中,单元测试通常针对单个组件或函数进行。
测试工具的选择
React 单元测试常用的工具包括 Jest 和 React Testing Library。Jest 是一个流行的 JavaScript 测试框架,React Testing Library 则专注于测试 React 组件的用户行为。
安装依赖
在项目中安装必要的测试依赖:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event
配置 Jest
在项目根目录下创建或修改 jest.config.js 文件:
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
};
编写测试文件
为组件编写测试文件,通常命名为 ComponentName.test.js。例如测试一个简单的按钮组件:
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('renders button with correct text', () => {
render(<Button>Click me</Button>);
const buttonElement = screen.getByText(/click me/i);
expect(buttonElement).toBeInTheDocument();
});
test('calls onClick prop when clicked', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
fireEvent.click(screen.getByText(/click me/i));
expect(handleClick).toHaveBeenCalledTimes(1);
});
测试异步操作
对于涉及异步操作的组件,使用 async/await 进行测试:

test('loads and displays data', async () => {
render(<FetchData />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
await waitFor(() => screen.getByText(/data loaded/i));
expect(screen.getByText(/data loaded/i)).toBeInTheDocument();
});
模拟数据和函数
使用 Jest 的 mock 功能模拟依赖项:
jest.mock('./api', () => ({
fetchData: jest.fn(() => Promise.resolve({ data: 'mock data' })),
}));
test('displays mock data', async () => {
render(<DataDisplay />);
await waitFor(() => screen.getByText(/mock data/i));
expect(screen.getByText(/mock data/i)).toBeInTheDocument();
});
快照测试
Jest 提供快照测试功能,用于确保 UI 不会意外改变:
test('matches snapshot', () => {
const { asFragment } = render(<Component />);
expect(asFragment()).toMatchSnapshot();
});
运行测试
在 package.json 中添加测试脚本:

"scripts": {
"test": "jest",
"test:watch": "jest --watch"
}
运行测试:
npm test
测试覆盖率
生成测试覆盖率报告:
npm test -- --coverage
最佳实践
- 测试用户行为而非实现细节
- 避免过度依赖快照测试
- 保持测试独立且可重复
- 为每个测试用例编写清晰的描述
- 模拟外部依赖以保持测试确定性
常见测试场景
测试表单组件:
test('form submission', async () => {
const handleSubmit = jest.fn();
render(<Form onSubmit={handleSubmit} />);
const input = screen.getByLabelText(/username/i);
fireEvent.change(input, { target: { value: 'testuser' } });
fireEvent.click(screen.getByText(/submit/i));
expect(handleSubmit).toHaveBeenCalledWith({ username: 'testuser' });
});
测试路由组件:
test('navigates to about page', () => {
render(
<MemoryRouter>
<App />
</MemoryRouter>
);
fireEvent.click(screen.getByText(/about/i));
expect(screen.getByText(/about page/i)).toBeInTheDocument();
});





