17.2 单元测试(Jest)
17.2 单元测试(Jest)
单元测试是现代 JavaScript 工具库开发中不可或缺的环节,Jest 是当前最流行的测试框架之一。本节将详细介绍如何使用 Jest 为你的工具库建立完整的测试体系。
为什么选择 Jest?
- 零配置:开箱即用,大部分场景无需额外配置
- 快速交互:智能监控文件变化,只运行相关测试
- 快照测试:轻松验证 UI 或复杂数据结构
- 强大 Mock:完整的模拟系统
- 覆盖率报告:内置覆盖率统计功能
基础环境搭建
首先安装 Jest:
npm install --save-dev jest @types/jest
在 package.json 中添加测试脚本:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
基本测试结构
创建 __tests__ 目录或在文件旁创建 .test.js 文件:
// math.utils.js
export function sum(a, b) {
return a + b;
}
// math.utils.test.js
import { sum } from '../math.utils';
describe('Math Utilities', () => {
test('adds two numbers correctly', () => {
expect(sum(1, 2)).toBe(3);
});
test('handles decimal numbers', () => {
expect(sum(0.1, 0.2)).toBeCloseTo(0.3);
});
});
常用断言方法
| 断言方法 | 用途 |
|---|---|
toBe() |
严格相等(===) |
toEqual() |
深度递归比较对象 |
toBeTruthy() |
检查是否为真值 |
toHaveLength() |
检查数组/字符串长度 |
toThrow() |
检查是否抛出错误 |
toMatchSnapshot() |
快照测试 |
toHaveBeenCalledWith() |
检查函数调用参数 |
测试异步代码
- Promise 测试:
test('fetches data successfully', () => {
return fetchData().then(data => {
expect(data).toHaveProperty('success', true);
});
});
- async/await 测试:
test('fetches data with async/await', async () => {
const data = await fetchData();
expect(data.user).toEqual('admin');
});
- 回调函数测试:
test('callback style test', done => {
fetchData(data => {
expect(data).toBeDefined();
done(); // 必须调用 done()
});
});
Mock 技术深度应用
- 函数 Mock:
const mockFn = jest.fn();
mockFn('arg1');
expect(mockFn).toHaveBeenCalledWith('arg1');
- 模块 Mock:
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve({ data: 'mock data' }))
}));
- 定时器 Mock:
jest.useFakeTimers();
setTimeout(() => console.log('done'), 1000);
jest.advanceTimersByTime(1000); // 快进时间
高级测试技巧
- 参数化测试:
describe.each([
[1, 1, 2],
[1, 2, 3],
[2, 2, 4]
])('add(%i, %i)', (a, b, expected) => {
test(`returns ${expected}`, () => {
expect(a + b).toBe(expected);
});
});
- 生命周期钩子:
beforeAll(() => console.log('在所有测试之前运行'));
afterEach(() => console.log('在每个测试之后运行'));
- 测试环境隔离:
describe('database', () => {
beforeAll(() => setupTestDatabase());
afterAll(() => tearDownTestDatabase());
});
测试覆盖率配置
在 jest.config.js 中配置:
module.exports = {
collectCoverage: true,
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
coveragePathIgnorePatterns: [
'/node_modules/',
'/__tests__/',
'index.js'
]
};
与 TypeScript 集成
- 安装依赖:
npm install --save-dev ts-jest @types/jest
- 配置
jest.config.js:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
globals: {
'ts-jest': {
diagnostics: false
}
}
};
测试工具库的特别考量
- 浏览器 API 模拟:
beforeEach(() => {
window.localStorage = {
store: {},
getItem(key) {
return this.store[key] || null;
},
setItem(key, value) {
this.store[key] = String(value);
},
clear() {
this.store = {};
}
};
});
- 性能测试:
test('should execute in less than 100ms', () => {
const start = performance.now();
heavyCalculation();
const end = performance.now();
expect(end - start).toBeLessThan(100);
});
常见问题解决方案
- ES Module 导入问题:
// jest.config.js
module.exports = {
transform: {
'^.+\\.js$': 'babel-jest'
}
};
- CSS/静态资源处理:
module.exports = {
moduleNameMapper: {
'\\.(css|less)$': 'identity-obj-proxy',
'\\.(jpg|png)$': '<rootDir>/__mocks__/fileMock.js'
}
};
- 环境变量问题:
// jest.setup.js
process.env.API_URL = 'http://test.example.com';
最佳实践建议
-
测试命名规范:
- 测试文件:
[name].test.js或[name].spec.js - 测试描述:
describe('模块/功能名称') - 测试用例:
test('应该...当...')
- 测试文件:
-
测试组织原则:
- 每个测试只验证一个行为
- 避免测试内部实现细节
- 优先测试公共接口
-
测试数据管理:
- 使用工厂函数生成测试数据
- 考虑使用 Faker.js 生成随机数据
- 对大型测试数据使用
beforeAll共享
通过以上配置和实践,你可以为你的工具库建立完善的测试体系。接下来我们将学习如何将你的库发布到 npm 仓库。
#前端开发
分享于 2025-03-25
上一篇:17.1 使用 Rollup 打包
下一篇:17.3 发布到 npm