Jest 从入门到精通:一张图看懂原理,彻底掌握测试(最新版)
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),然后解析模块,最后执行
describe、test,配合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 的精髓。
流程解读(结合实践):
- 文件发现:Jest 启动后,HasteMap 会迅速扫描项目中的
.js、.jsx、.ts等文件,建立文件之间的依赖关系图。这个图会被缓存到node_modules/.cache/jest里,所以二次启动几乎瞬间完成。 - 变化检测:如果你开启了
--watch,Jest 会监听文件变动,并通过 HasteMap 精确知道哪些文件修改了,从而只运行相关的测试(--onlyChanged)。这也是 Jest 在开发模式下体验极好的原因。 - 任务分配:TestScheduler 根据文件数量和 CPU 核心数,将测试文件分配给不同的 Worker 进程。每个 Worker 独立运行,环境隔离,避免了测试之间的全局污染。
- 并行执行:每个 Worker 内部,先对文件进行转换(比如 TypeScript → JS),然后在沙箱中加载并运行测试代码。
- 结果汇总:所有 Worker 执行完毕后,主进程收集结果,统一由 Reporter 输出。
- 智能重跑:如果开启了
--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 核心机制

解读:
jest.mock('axios')告诉 Jest,这个模块需要被 Mock。- Jest 内部创建一个代理模块,替换了模块缓存中的原始
axios。 - 测试代码中导入的
axios实际上是这个代理模块。 - 通过
mockedAxios.get.mockResolvedValue设置代理模块的行为。 - 被测试的代码在运行时,导入的永远是 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 Runner 或 vscode-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-dom、jest-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、覆盖率阈值调优等),欢迎直接在评论区贴代码,我会尽量帮你分析并提供解决方案。
开始愉悦的测试之旅吧! 🚀
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)