服务端阅读 05月27日 19:51
什么是 Jest 测试框架?它有哪些核心特性?
Jest 是由 Facebook(Meta)开发的 JavaScript 测试框架,凭借零配置、内置工具链和出色的开发者体验,已成为前端领域使用率最高的测试框架。根据 State of JS 调查,Jest 的使用率从 2016 年的 8% 增长到 2021 年的 73%,被 Facebook、Airbnb、Spotify 等公司广泛采用。核心特性1. 零配置开箱即用Jest 内置了测试运行器、断言库、Mock 系统、代码覆盖率工具和快照测试功能,无需安装和配置额外依赖即可开始编写测试:npm install --save-dev jest在 package.json 中添加测试脚本后即可运行:{ "scripts": { "test": "jest" }}2. 内置断言库与丰富的匹配器Jest 提供了 expect 断言函数和大量匹配器(matchers),覆盖常见断言场景: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 最核心的能力之一,可以模拟函数、模块和定时器,隔离被测代码的外部依赖:// 模拟函数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); // 返回 3expect(calc).toHaveBeenCalledWith(1, 2);4. 快照测试快照测试用于捕获组件或函数的输出,在后续运行中对比是否发生变化,特别适合 UI 组件测试:test('按钮组件快照', () => { const { container } = render(<Button label="Submit" />); expect(container).toMatchSnapshot();});首次运行会生成 .snap 快照文件,后续运行自动对比。如果变化是预期的,使用 jest --updateSnapshot 更新。5. 并行执行与性能优化Jest 自动并行执行测试文件,利用 Worker 进程充分使用多核 CPU,显著提高测试速度。还支持以下优化策略:--onlyChanged:只运行受改动影响的测试文件--findRelatedTests:运行与指定文件相关的测试缓存机制:自动缓存未变更文件的测试结果6. 内置代码覆盖率无需额外安装 Istanbul 等工具,Jest 内置覆盖率统计:jest --coverage可生成行覆盖率、分支覆盖率、函数覆盖率和语句覆盖率报告,支持 HTML 可视化输出。核心概念测试组织结构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 支持多种异步测试方式:// 回调方式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() 恢复const 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 作为替代。