一、Jest 配置
1. 项目依赖配置
1.1 依赖管理
{
"dependencies": {
"jest": "^30.0.5", // Jest 测试框架
"ts-jest": "^29.4.1", // TypeScript 支持
"@types/jest": "^30.0.0" // Jest TypeScript 类型
},
"devDependencies": {
"@testing-library/jest-dom": "^6.7.0", // DOM 测试增强工具
"@testing-library/react": "^13.4.0", // React 测试工具
"entities": "^6.0.1", // HTML 实体解析
"identity-obj-proxy": "^3.0.0" // 样式文件模拟
}
}
1.2 Jest 配置 (package.json)
{
"jest": {
"testEnvironment": "jsdom", // 使用 jsdom 环境模拟浏览器
"setupFilesAfterEnv": [
"<rootDir>/setupTests.js" // 测试环境设置文件
],
"moduleNameMapper": {
"\\\\.(css|less)$": "identity-obj-proxy" // 处理样式文件
},
"coverageDirectory": "coverage", // 测试覆盖率报告目录
"collectCoverage": true // 启用覆盖率收集
}
}
2. 环境配置
2.1 设置测试环境 (setupTests.js)
// 导入 DOM 测试工具
import '@testing-library/jest-dom';
// 模拟 window.matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// 模拟 ResizeObserver
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
二、实际应用案例
案例 1:React 组件测试
需求
测试一个简单的计数器组件,能够增加和减少计数。
组件代码
// Counter.js
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(count - 1)}>Decrease</button>
</div>
);
};
export default Counter;
测试代码
使用 render 和 fireEvent 方法来测试组件的功能。
// Counter.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Counter from './Counter';
test('initial count is 0', () => {
const { getByText } = render(<Counter />);
expect(getByText('0')).toBeInTheDocument();
});
test('increases count by 1 when Increase button is clicked', () => {
const { getByText } = render(<Counter />);
fireEvent.click(getByText('Increase'));
expect(getByText('1')).toBeInTheDocument();
});
test('decreases count by 1 when Decrease button is clicked', () => {
const { getByText } = render(<Counter />);
fireEvent.click(getByText('Increase'));
fireEvent.click(getByText('Decrease'));
expect(getByText('0')).toBeInTheDocument();
});
案例 2:API 测试
需求
测试一个异步函数,从 API 获取数据。
API 函数代码
// api.js
export const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
测试代码
使用 Mock 函数来模拟 API 响应。
// api.test.js
import { fetchData } from './api';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: 'mock data' }),
})
);
test('fetches successfully data from an API', async () => {
const data = await fetchData();
expect(data).toEqual({ data: 'mock data' });
});
test('fetch fails with an error', async () => {
fetch.mockImplementationOnce(() => Promise.reject('API is down'));
await expect(fetchData()).rejects.toEqual('API is down');
});
案例 3:快照测试
组件代码
// Button.js
import React from 'react';
const Button = ({ label }) => (
<button>{label}</button>
);
export default Button;
测试代码
使用快照测试验证组件的输出。
// Button.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import Button from './Button';
test('Button renders correctly', () => {
const tree = renderer.create(<Button label="Click me" />).toJSON();
expect(tree).toMatchSnapshot();
});
三、常用 API 和方法
1. 测试用例 API
test(name, fn):定义一个测试用例。it(name, fn):与test等效,描述行为。
2. 匹配器
-
expect(value):用于断言某个值。 -
常用匹配器:
toBe(value):严格相等。toEqual(value):深度相等。toBeTruthy():值为真。toContain(item):检查数组或字符串中是否包含某个项。toMatchSnapshot():进行快照测试。
3. 异步匹配
resolves:测试 Promise 的成功结果。rejects:测试 Promise 的失败结果。
4. Mock 函数 API
jest.fn():创建一个新的 Mock 函数。mockReturnValue(value):设置 Mock 函数的返回值。mockImplementation(fn):设置 Mock 函数的实现。mock.calls:获取 Mock 函数的调用记录。
5. 组装和清理 API
beforeEach(fn):在每个测试用例之前运行的函数。afterEach(fn):在每个测试用例之后运行的函数。beforeAll(fn):在所有测试用例之前运行的函数。afterAll(fn):在所有测试用例之后运行的函数。
6. 运行测试的命令
jest --watch:以观察模式运行测试。jest --coverage:生成代码覆盖率报告。