5月27日 19:51

什么是 Jest 测试框架?它有哪些核心特性?

Jest 是由 Facebook(Meta)开发的 JavaScript 测试框架,凭借零配置、内置工具链和出色的开发者体验,已成为前端领域使用率最高的测试框架。根据 State of JS 调查,Jest 的使用率从 2016 年的 8% 增长到 2021 年的 73%,被 Facebook、Airbnb、Spotify 等公司广泛采用。

核心特性

1. 零配置开箱即用

Jest 内置了测试运行器、断言库、Mock 系统、代码覆盖率工具和快照测试功能,无需安装和配置额外依赖即可开始编写测试:

bash
npm install --save-dev jest

package.json 中添加测试脚本后即可运行:

json
{ "scripts": { "test": "jest" } }

2. 内置断言库与丰富的匹配器

Jest 提供了 expect 断言函数和大量匹配器(matchers),覆盖常见断言场景:

javascript
test('基础匹配器示例', () => { // 相等性判断 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 组件测试:

javascript
test('按钮组件快照', () => { const { container } = render(<Button label="Submit" />); expect(container).toMatchSnapshot(); });

首次运行会生成 .snap 快照文件,后续运行自动对比。如果变化是预期的,使用 jest --updateSnapshot 更新。

5. 并行执行与性能优化

Jest 自动并行执行测试文件,利用 Worker 进程充分使用多核 CPU,显著提高测试速度。还支持以下优化策略:

  • --onlyChanged:只运行受改动影响的测试文件
  • --findRelatedTests:运行与指定文件相关的测试
  • 缓存机制:自动缓存未变更文件的测试结果

6. 内置代码覆盖率

无需额外安装 Istanbul 等工具,Jest 内置覆盖率统计:

bash
jest --coverage

可生成行覆盖率、分支覆盖率、函数覆盖率和语句覆盖率报告,支持 HTML 可视化输出。

核心概念

测试组织结构

javascript
describe('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() 恢复
javascript
const spy = jest.spyOn(console, 'log').mockImplementation(() => {}); // 测试结束后恢复 spy.mockRestore();

快照测试的局限是什么?

  • 快照可能过于宽泛,导致即使有 bug 也通过对比
  • 大型快照难以 review,容易盲目更新
  • 不适合频繁变更的 UI 或动态数据

最佳实践:保持快照小而精确,使用 toMatchSnapshot 配合自定义匹配器。

与其他框架对比

特性JestMochaVitest
配置零配置需搭配 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 作为替代。

标签:Jest