Appearance
Jest
简介
Jest 是 Facebook 开发的一个功能全面的 JavaScript 测试框架,专注于简单性。它内置了断言、测试覆盖率、Mock 功能、快照测试等特性,几乎不需要配置就可以开始使用,是目前最流行的 JavaScript 测试解决方案之一。
核心特性
零配置测试平台
Jest 提供了开箱即用的体验,无需复杂配置:
bash
# 安装 Jest
npm install --save-dev jest
# 添加测试脚本到 package.json
{
"scripts": {
"test": "jest"
}
}
# 运行测试
npm test
快照测试
快照测试可以捕获组件或数据结构的渲染结果,并将其与之前保存的快照进行比较:
javascript
it('renders correctly', () => {
const tree = renderer
.create(<Link page="https://example.com">Example</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});
Mock 功能
Jest 提供了强大的 Mock 功能,可以模拟函数、模块和定时器:
javascript
// 模拟函数
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
console.log(mockFn()); // 42
// 模拟模块
jest.mock('./math');
// 模拟定时器
jest.useFakeTimers();
setTimeout(() => {}, 1000);
jest.runAllTimers();
基本用法
编写测试
javascript
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
// sum.test.js
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
测试组织
javascript
describe('计算器功能测试', () => {
beforeEach(() => {
// 每个测试前的设置
});
afterEach(() => {
// 每个测试后的清理
});
test('加法运算', () => {
expect(sum(1, 2)).toBe(3);
});
test('减法运算', () => {
expect(subtract(5, 2)).toBe(3);
});
});
常用匹配器
javascript
// 精确匹配
expect(value).toBe(2); // 使用 Object.is 比较
expect(value).toEqual({ a: 1 }); // 递归比较对象的值
// 真值检查
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
// 数字比较
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(5);
// 字符串
expect('team').toMatch(/tea/);
// 数组
expect(['apple', 'banana']).toContain('apple');
// 异常
expect(() => { throw new Error('错误') }).toThrow();
高级功能
异步测试
javascript
// Promise
test('异步数据获取', () => {
return fetchData().then(data => {
expect(data).toBe('peanut butter');
});
});
// Async/Await
test('异步数据获取', async () => {
const data = await fetchData();
expect(data).toBe('peanut butter');
});
// 回调
test('异步回调', done => {
function callback(data) {
try {
expect(data).toBe('peanut butter');
done();
} catch (error) {
done(error);
}
}
fetchData(callback);
});
测试覆盖率
bash
# 运行测试并收集覆盖率
npm test -- --coverage
javascript
// jest.config.js
module.exports = {
collectCoverage: true,
collectCoverageFrom: [
'**/*.{js,jsx}',
'!**/node_modules/**',
'!**/vendor/**'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
测试 React 组件
javascript
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('按钮点击事件', () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>点击我</Button>);
fireEvent.click(screen.getByText('点击我'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
配置选项
jest.config.js
javascript
module.exports = {
// 测试环境
testEnvironment: 'jsdom',
// 测试文件匹配模式
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
// 转换器
transform: {
'^.+\.jsx?$': 'babel-jest',
'^.+\.tsx?$': 'ts-jest'
},
// 模块名映射
moduleNameMapper: {
'\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
'\.(jpg|jpeg|png|gif)$': '<rootDir>/__mocks__/fileMock.js',
'^@/(.*)$': '<rootDir>/src/$1'
},
// 设置测试超时时间(毫秒)
testTimeout: 10000
};
最佳实践
测试组织
- 使用
describe
对相关测试进行分组 - 使用
beforeEach
和afterEach
设置和清理测试环境 - 保持测试独立,避免测试间的依赖
命名约定
- 测试文件使用
.test.js
或.spec.js
后缀 - 测试目录使用
__tests__
命名 - 测试描述应清晰表达预期行为
Mock 策略
- 尽量模拟外部依赖,如 API 调用、数据库访问
- 使用
jest.spyOn
监视对象方法的调用 - 使用
jest.mock
替换整个模块的实现
常见问题与解决方案
测试超时
javascript
// 增加单个测试的超时时间
test('长时间运行的测试', () => {
// ...
}, 30000); // 30秒
// 或在配置中全局设置
// jest.config.js
module.exports = {
testTimeout: 30000
};
模块路径解析
javascript
// jest.config.js
module.exports = {
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
};
测试环境问题
javascript
// 设置不同的测试环境
// jest.config.js
module.exports = {
testEnvironment: 'node', // 或 'jsdom'
};
与其他测试工具对比
特性 | Jest | Mocha | Vitest |
---|---|---|---|
开箱即用 | 优秀 | 需配置 | 优秀 |
执行速度 | 良好 | 良好 | 极快 |
内置断言 | 是 | 否 | 是 |
内置覆盖率 | 是 | 否 | 是 |
快照测试 | 支持 | 不支持 | 支持 |
并行测试 | 支持 | 支持 | 支持 |
社区生态 | 丰富 | 丰富 | 成长中 |