什么是 Jest 测试框架?它有哪些核心特性?
Jest 是由 Facebook(Meta)开发的 JavaScript 测试框架,凭借零配置、内置工具链和出色的开发者体验,已成为前端领域使用率最高的测试框架。根据 State of JS 调查,Jest 的使用率从 2016 年的 8% 增长到 2021 年的 73%,被 Facebook、Airbnb、Spotify 等公司广泛采用。
核心特性
1. 零配置开箱即用
Jest 内置了测试运行器、断言库、Mock 系统、代码覆盖率工具和快照测试功能,无需安装和配置额外依赖即可开始编写测试:
bashnpm install --save-dev jest
在 package.json 中添加测试脚本后即可运行:
json{ "scripts": { "test": "jest" } }
2. 内置断言库与丰富的匹配器
Jest 提供了 expect 断言函数和大量匹配器(matchers),覆盖常见断言场景:
javascripttest('基础匹配器示例', () => { // 相等性判断 expect(1 + 1).toBe(2); // 严格相等 === expect({ a: 1 }).toEqual({ a: 1 }); // 深度相等 // 真值判断 expect(true).toBeTruthy(); expect(null).toBeFalsy(); expect(undefined).toBeUndefined(); expect(1).toBeDefined(); // 数字比较 expect(0.1 + 0.2).toBeCloseTo(0.3); // 浮点数近似 expect(5).toBeGreaterThan(3); // 字符串匹配 expect('hello world').toMatch(/world/); // 数组与异常 expect([1, 2, 3]).toContain(2); expect(() => { throw new Error('err'); }).toThrow('err'); });
3. 强大的 Mock 系统
Mock 是 Jest 最核心的能力之一,可以模拟函数、模块和定时器,隔离被测代码的外部依赖:
javascript// 模拟函数 const mockFn = jest.fn(); mockFn.mockReturnValue(42); mockFn(); // 返回 42 // 验证调用情况 expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledTimes(1); // 模拟模块 jest.mock('axios'); axios.get.mockResolvedValue({ data: { name: 'test' } }); // 模拟实现 const calc = jest.fn((a, b) => a + b); calc(1, 2); // 返回 3 expect(calc).toHaveBeenCalledWith(1, 2);
4. 快照测试
快照测试用于捕获组件或函数的输出,在后续运行中对比是否发生变化,特别适合 UI 组件测试:
javascripttest('按钮组件快照', () => { const { container } = render(<Button label="Submit" />); expect(container).toMatchSnapshot(); });
首次运行会生成 .snap 快照文件,后续运行自动对比。如果变化是预期的,使用 jest --updateSnapshot 更新。
5. 并行执行与性能优化
Jest 自动并行执行测试文件,利用 Worker 进程充分使用多核 CPU,显著提高测试速度。还支持以下优化策略:
--onlyChanged:只运行受改动影响的测试文件--findRelatedTests:运行与指定文件相关的测试- 缓存机制:自动缓存未变更文件的测试结果
6. 内置代码覆盖率
无需额外安装 Istanbul 等工具,Jest 内置覆盖率统计:
bashjest --coverage
可生成行覆盖率、分支覆盖率、函数覆盖率和语句覆盖率报告,支持 HTML 可视化输出。
核心概念
测试组织结构
javascriptdescribe('Calculator', () => { beforeAll(() => { /* 所有测试前执行一次 */ }); afterAll(() => { /* 所有测试后执行一次 */ }); beforeEach(() => { /* 每个测试前执行 */ }); afterEach(() => { /* 每个测试后执行 */ }); test('adds two numbers', () => { expect(add(1, 2)).toBe(3); }); test('subtracts two numbers', () => { expect(subtract(5, 3)).toBe(2); }); });
describe:将相关测试用例分组,支持嵌套test/it:定义单个测试用例- 钩子函数:
beforeAll/afterAll/beforeEach/afterEach管理测试生命周期
异步测试
Jest 支持多种异步测试方式:
javascript// 回调方式 test('callback', (done) => { fetchData((data) => { expect(data).toBe('result'); done(); }); }); // Promise 方式 test('promise', () => { return fetchData().then(data => { expect(data).toBe('result'); }); }); // async/await 方式(推荐) test('async/await', async () => { const data = await fetchData(); expect(data).toBe('result'); }); // resolves/rejects 匹配器 test('resolves matcher', () => { return expect(fetchData()).resolves.toBe('result'); });
面试常见追问
Jest 的测试隔离机制是什么?
每个测试文件在独立的模块作用域中执行,beforeEach/afterEach 确保测试之间状态不共享。Jest 通过 jest.isolateModules() 或自动的模块注册表隔离来防止测试间污染。
spyOn 和 jest.fn() 有什么区别?
jest.fn()创建全新的模拟函数,不保留原始实现jest.spyOn(object, method)包装对象的现有方法,保留原始实现,可通过.mockImplementation()替换,用.mockRestore()恢复
javascriptconst spy = jest.spyOn(console, 'log').mockImplementation(() => {}); // 测试结束后恢复 spy.mockRestore();
快照测试的局限是什么?
- 快照可能过于宽泛,导致即使有 bug 也通过对比
- 大型快照难以 review,容易盲目更新
- 不适合频繁变更的 UI 或动态数据
最佳实践:保持快照小而精确,使用 toMatchSnapshot 配合自定义匹配器。
与其他框架对比
| 特性 | Jest | Mocha | Vitest |
|---|---|---|---|
| 配置 | 零配置 | 需搭配 chai/sinon/nyc | 兼容 Jest API,零配置 |
| 断言库 | 内置 | 需额外安装 | 内置 |
| Mock | 内置 | 需搭配 Sinon | 内置 |
| 快照测试 | 内置 | 需额外插件 | 内置 |
| 执行速度 | 快(并行) | 较慢 | 最快(ESM 原生) |
| ESM 支持 | 实验性 | 支持 | 原生支持 |
| 生态成熟度 | 最成熟 | 成熟 | 快速增长 |
Vitest 是 Jest 的新兴替代,与 Vite 生态深度整合,在 ESM 原生支持和执行速度上有优势,但 Jest 的生态和社区资源仍然最为丰富。
总结
Jest 的核心优势在于"一站式"测试体验——内置断言、Mock、快照和覆盖率,零配置即可运行,并行执行保证速度。面试中需重点掌握 Mock 系统(jest.fn/jest.spyOn/jest.mock)、异步测试三种方式和快照测试原理。在新项目中如果使用 Vite,可以优先考虑 Vitest 作为替代。