Jest 从入门到精通:一张图看懂原理,彻底掌握测试(最新版)

用 Jest 写测试好几年了,从最早的 24 版本一路追到现在的 30.3.0,每次大版本升级都能感受到团队在“开发者体验”上的用心。最近帮团队做 Jest 30 升级,顺手整理了一份“从原理到实践”的完整指南。希望能帮你不仅会用 Jest,还能真正理解它为什么快、为什么稳,遇到问题能自己排查。

在这里插入图片描述

1. Jest 是什么?先看一张整体架构图

Jest 是 Meta 开源的一个 JavaScript 测试框架,主打“零配置、速度快、功能全”。它内置了测试运行器、断言库、Mock 工具、覆盖率报告、快照测试……几乎你需要的所有东西都打包好了,一条命令就能跑起来。

为了让你从宏观上理解 Jest 的内部构造,我画了下面这张整体架构图。看了它,你就知道 Jest 各个模块是怎么协同工作的:

在这里插入图片描述

图解一下

  • 命令行入口:执行 jest 后,CLI 解析参数并与全局配置合并。
  • 核心调度层
    • HasteMap:Jest 独有的文件映射系统,它会快速扫描项目文件,建立依赖关系图并缓存。这也是 Jest 启动飞快的原因——二次运行几乎瞬间完成。
    • TestScheduler:根据文件变更情况和配置,智能决定哪些测试需要跑,怎么分配给 Worker。
  • Worker 进程池:Jest 默认会启动多个 Worker(数量等于 CPU 核心数),每个 Worker 在独立的 Node 进程中运行,完全隔离,互不影响。
  • Worker 内部:每个 Worker 包含完整的测试环境:先转换代码(比如 TypeScript、Babel),然后解析模块,最后执行 describetest,配合 expect、Mock 和快照。
  • 报告输出:所有 Worker 跑完后,汇总结果给 Reporter 输出到终端或文件。

这张图基本涵盖了 Jest 的核心模块,后面我们深入每一步的时候都可以对照着看。


2. 3 分钟上手:从零跑通第一个测试

这部分和以前版本完全一样,Jest 30 依然保持零配置的优良传统。

npm install --save-dev jest
// 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);
});
// package.json
{
  "scripts": {
    "test": "jest"
  }
}

运行 npm test,你会看到绿色的 PASS。是不是很简单?


3. 核心 API 快速回顾(附实用代码)

如果你已经熟悉 Jest,可以直接跳到第 4 节。这里简单过一遍最常用的 API,方便查阅。

3.1 分组与测试

describe('算术运算', () => {
  test('加法', () => {
    expect(1 + 2).toBe(3);
  });

  it('减法', () => {   // it 是 test 的别名
    expect(5 - 3).toBe(2);
  });
});

3.2 常用匹配器

expect(value).toBe(3);               // 严格相等(Object.is)
expect(object).toEqual({ a: 1 });    // 递归比较对象内容
expect(array).toContain(2);           // 包含
expect(fn).toThrow();                  // 抛出异常

3.3 异步测试

// 方式一:async/await
test('异步请求', async () => {
  const data = await fetchData();
  expect(data).toBe('hello');
});

// 方式二:done 回调
test('异步请求', (done) => {
  fetchData().then(data => {
    expect(data).toBe('hello');
    done();
  });
});

3.4 Mock 函数

const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn('arg1');
expect(mockFn).toHaveBeenCalledWith('arg1');

3.5 快照测试(常用于 UI 组件)

import renderer from 'react-test-renderer';
test('组件快照', () => {
  const tree = renderer.create(<MyComponent />).toJSON();
  expect(tree).toMatchSnapshot();
});

第一次运行会生成 .snap 文件,后续运行对比,不一致则报错。用 jest -u 更新快照。

3.6 模块 Mock

jest.mock('axios');   // 整个 axios 模块被自动替换为 Mock 版本
const axios = require('axios');
axios.get.mockResolvedValue({ data: {} });

4. Jest 执行流程原理图(为什么这么快?)

下面这张图是我花了不少心思整理的,详细展示了一次 jest 命令从启动到输出报告的完整流程。理解了这张图,你就掌握了 Jest 的精髓。
在这里插入图片描述
流程解读(结合实践):

  1. 文件发现:Jest 启动后,HasteMap 会迅速扫描项目中的 .js.jsx.ts 等文件,建立文件之间的依赖关系图。这个图会被缓存到 node_modules/.cache/jest 里,所以二次启动几乎瞬间完成
  2. 变化检测:如果你开启了 --watch,Jest 会监听文件变动,并通过 HasteMap 精确知道哪些文件修改了,从而只运行相关的测试--onlyChanged)。这也是 Jest 在开发模式下体验极好的原因。
  3. 任务分配:TestScheduler 根据文件数量和 CPU 核心数,将测试文件分配给不同的 Worker 进程。每个 Worker 独立运行,环境隔离,避免了测试之间的全局污染。
  4. 并行执行:每个 Worker 内部,先对文件进行转换(比如 TypeScript → JS),然后在沙箱中加载并运行测试代码。
  5. 结果汇总:所有 Worker 执行完毕后,主进程收集结果,统一由 Reporter 输出。
  6. 智能重跑:如果开启了 --watch,Jest 会记住上一次失败的测试,并优先重跑它们(--onlyFailures)。这个功能在修复 bug 时非常实用。

为什么 Jest 比 Mocha 快?
核心就在于三点:HasteMap 缓存 + 多 Worker 隔离 + 智能调度。Mocha 通常需要你手动配置慢的环节(比如文件查找、并行),而 Jest 把这些都内建好了,开箱即用。


5. 配置详解:TypeScript + React + ESM(Jest 30 新特性)

Jest 30 最让我惊喜的是对 ESM 的原生支持 更稳定了,配合 TypeScript 和 React 非常丝滑。下面是一个融合了这些现代特性的 jest.config.js 示例(用了 defineConfig,享受类型提示):

import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
import { defineConfig } from 'jest'; // 类型安全

export default defineConfig({
  // 使用 ts-jest 的 ESM 预设
  preset: 'ts-jest/presets/default-esm',
  testEnvironment: 'jsdom',               // React 组件需要
  setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
  moduleNameMapper: {
    // 静态资源 Mock
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    // 别名支持
    '^@/(.*)$': '<rootDir>/src/$1'
  },
  transform: {
    '^.+\\.tsx?$': [
      'ts-jest',
      { useESM: true } // 开启 ESM 支持
    ]
  },
  extensionsToTreatAsEsm: ['.ts', '.tsx'],
  // 覆盖率收集与阈值
  collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  },
  // watch 模式增强插件
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname'
  ]
});

Jest 30 的几个新特性

  • 原生 ESM:在 transform 中设置 useESM: true 即可,不再需要 --experimental-vm-modules
  • defineConfig:带类型提示的配置函数,告别手写 JSON 的拼写错误。
  • watch 插件增强jest-watch-typeahead 可以让你在 watch 模式下,按文件名或测试名快速过滤,特别适合大项目。

6. 高级原理:快照和 Mock 是如何工作的?

对于想深入理解的读者,我画了两张原理图,分别解释快照测试和 Mock 的核心机制。

6.1 快照测试原理

在这里插入图片描述

实践心得:快照测试最适合用来检测 UI 组件或配置文件的意外变更。但要注意,快照文件应该提交到 Git 仓库,让团队成员都能看到变更。如果快照变更了,一定要用 jest -u 更新并仔细审查 diff。

6.2 Mock 核心机制

在这里插入图片描述

解读

  1. jest.mock('axios') 告诉 Jest,这个模块需要被 Mock。
  2. Jest 内部创建一个代理模块,替换了模块缓存中的原始 axios
  3. 测试代码中导入的 axios 实际上是这个代理模块。
  4. 通过 mockedAxios.get.mockResolvedValue 设置代理模块的行为。
  5. 被测试的代码在运行时,导入的永远是 Mock 版本,从而实现了隔离。

小技巧:如果只想 Mock 模块的某些方法,可以用 jest.spyOn


7. 最佳实践 + 调试 + CI + 对比 Vitest

7.1 覆盖率阈值强制

jest.config.js 里设置 coverageThreshold,如果低于阈值,CI 就会失败。这能有效防止覆盖率下降。

coverageThreshold: {
  global: {
    branches: 80,
    functions: 80,
    lines: 80,
    statements: 80
  }
}

7.2 开发体验优化

  • --watch 模式:配合上面提到的 jest-watch-typeahead 插件,可以快速按文件名或测试名过滤。
  • --onlyFailures:重跑上次失败的测试,开发时非常高效。
  • VS Code 插件:安装 Jest Runnervscode-jest,可以直接在编辑器内单点运行和调试测试。想断点调试的话,在测试文件里打 debugger,然后选择 Debug 选项即可。

7.3 CI 集成建议

  • 在 CI 中运行 jest --ci --coverage --maxWorkers=2(限制 CI 资源,避免占用过多)。
  • 将覆盖率报告上传到 Codecov 或 Coveralls,方便追踪趋势。

7.4 与 Vitest 的对比(2026 年)

这几年 Vitest 发展很快,很多新项目都在用。我简单对比一下:

特性 Jest Vitest
速度 快(HasteMap + 多进程) 极快(基于 Vite 的预编译和 ESBuild)
配置 零配置,但复杂项目需配置 几乎零配置,与 Vite 配置共享
生态 最成熟,插件丰富 迅速成长,兼容 Jest API
适用场景 任何项目,尤其是老项目 新 Vite 项目,追求极致速度

我的建议

  • 如果你正在维护一个老项目,或者项目已经用了很多 Jest 插件(比如 jest-domjest-extended),继续用 Jest 30 是稳妥的选择,它依然是最稳定、最成熟的那个。
  • 如果你是一个全新的 Vite 项目(比如用 Vite 搭建的 Vue 3 或 React 项目),可以优先考虑 Vitest,因为它与 Vite 无缝集成,速度更快,而且 API 完全兼容 Jest,迁移成本几乎为零。

每日目标:保持覆盖率 ≥ 80%,在关键模块上甚至 90%+。测试不是负担,而是让你放心重构的保障。


8. 总结

通过这篇文章,我们不仅回顾了 Jest 的基本用法,更重要的是通过几张原理图理解了它的内部机制:

  • 整体架构图让你看清模块关系;
  • 执行流程图解释了 Jest 为什么这么快;
  • 快照和 Mock 原理图让你深入理解两大核心功能。

掌握这些,你就不再只是“会用 Jest”,而是真正“懂 Jest”。遇到问题能自己分析,做配置能心里有数。

最后,如果你在实战中遇到任何具体问题(比如 React Testing Library 搭配、自定义 Transformer、迁移到 Vitest、覆盖率阈值调优等),欢迎直接在评论区贴代码,我会尽量帮你分析并提供解决方案。

开始愉悦的测试之旅吧! 🚀

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐