Jest 测试框架速查表 - JavaScript 单元测试 API 大全

测试只有在你失败时能明确指出"到底哪里坏了"才算值得写。本表覆盖 describe/it 分组、toBe 之外的匹配器、异步与 promise 测试、jest.fn/jest.mock 的模块模拟、快照测试与覆盖率输出。重点放在那些能把坏行为定位清楚、而不是给出含糊红光/红字的匹配器与 mock 策略上。想给 JS/TS 代码真加单元覆盖的团队最合适。读完能写出聚焦的断言,并精确 mock 你想隔离的那条接缝。

编程语言·共 44 条命令·最后更新 2026-07-21

典型使用场景

面向需要在 Node.js 或浏览器环境里给业务代码写可靠单元测试的开发者,尤其是要处理异步、定时器、模块依赖这些棘手场景时。本表覆盖四层能力:用 test / describe / only / skip 组织用例结构;用 toBe、toEqual、toHaveLength、toMatch 这组匹配器表达断言;用 async 回调、resolves / rejects、done 回调和假定时器处理异步;再用 jest.fn、jest.spyOn、jest.mock 隔离外部依赖,配合 toHaveBeenCalled 系列验证交互,最后用快照与 --coverage / --watch / --bail / -t 组成日常命令。易错点部分集中了 forgot to return 的异步漏等、mock 忘记还原污染后续用例、快照误提交、test.only 忘记删这几类会让 CI 变红的典型事故。

基础测试 6

test("描述", () => { expect(1 + 2).toBe(3) })
定义一个测试用例
it("描述", fn)
test 的别名,BDD 风格更自然
describe("模块", () => { it(...) })
将相关测试分组
test.skip("跳过", () => {})
临时跳过此测试
test.only("聚焦", () => {})
只运行此测试(调试用)
test.todo("待补充")
占位待实现的测试用例

匹配器 Matchers 8

expect(value).toBe(other)
严格相等(Object.is)
expect(obj).toEqual({ a: 1 })
深度相等(递归比较)
expect(arr).toHaveLength(3)
数组或字符串长度
expect(str).toMatch(/regex/)
正则匹配字符串
expect(arr).toContain(item)
数组包含某元素
expect(value).toBeTruthy()
真值判断(反之 toBeFalsy)
expect(fn).toThrow("error")
期望抛出异常(可匹配消息)
expect(value).toBeNull()
空值判断(另有 toBeUndefined/toBeDefined)

异步测试 6

test("async", async () => { await fn() })
async/await 写法
test("promise", () => fn().then(d => expect(d).toBe(1)))
返回 Promise 让 Jest 等待
expect(promise).resolves.toBe(value)
断言 resolve 的值
expect(promise).rejects.toThrow("err")
断言 reject 的原因
test("done", done => { fn(done) })
done 回调写法(需手动调用)
jest.useFakeTimers()
使用假定时器(配合 jest.runAllTimers())

钩子函数 Hooks 4

beforeEach(() => setup())
每个测试前执行
afterEach(() => cleanup())
每个测试后执行
beforeAll(() => init())
所有测试前执行一次
afterAll(() => teardown())
所有测试后执行一次

Mock 函数 8

const fn = jest.fn()
创建 Mock 函数
jest.fn().mockReturnValue(42)
设置固定返回值
jest.fn().mockResolvedValue(val)
返回 resolve 的 Promise
jest.fn().mockImplementation(n => n * 2)
自定义实现逻辑
jest.spyOn(obj, "method")
spy 对象方法(默认保留原实现)
jest.mock("./module")
自动 Mock 整个模块的所有导出
jest.requireActual("./module")
在 Mock 上下文中引入真实模块
fn.mockClear() / mockReset() / mockRestore()
清理调用记录与实现

断言与调用验证 5

expect(fn).toHaveBeenCalled()
验证函数被调用过
expect(fn).toHaveBeenCalledWith(arg)
验证调用参数
expect(fn).toHaveBeenCalledTimes(2)
验证调用次数
expect(fn).toHaveBeenLastCalledWith(arg)
验证最后一次调用参数
expect(obj).toMatchObject({ a: 1 })
对象部分匹配

快照测试与 CLI 7

expect(tree).toMatchSnapshot()
创建/对比快照(更新加 -u)
expect(obj).toMatchInlineSnapshot()
内联快照(写入测试文件)
expect(value).toBeInstanceOf(Class)
实例类型断言
npx jest --coverage
生成覆盖率报告
npx jest --watch
监听改动重跑相关测试(--watchAll 全部)
npx jest --bail
首个失败立即停止
npx jest -t "用例名"
按用例名正则过滤运行

参数矩阵

参数作用示例
test("描述", fn)定义一个测试用例test("描述", () => { expect(1 + 2).toBe(3) })
it("描述", fn)test 的别名,BDD 风格读起来更自然it("描述", fn)
describe("模块", fn)把相关用例分组,输出层次更清晰describe("模块", () => { it(...) })
test.skip临时跳过此用例test.skip("跳过", () => {})
test.only只运行此用例,调试用test.only("聚焦", () => {})
test.todo占位待实现的用例test.todo("用例名")
expect(v).toBe(x)严格相等(Object.is)expect(value).toBe(other)
expect(obj).toEqual({ a: 1 })深度相等,递归比较expect(obj).toEqual({ a: 1 })
expect(arr).toHaveLength(3)断言数组或字符串长度expect(arr).toHaveLength(3)
expect(str).toMatch(/regex/)正则匹配字符串expect(str).toMatch(/regex/)
expect(arr).toContain(item)断言数组包含某元素expect(arr).toContain(item)
expect(v).toBeTruthy() / toBeFalsy()真值 / 假值判断expect(value).toBeTruthy()
expect(fn).toThrow("error")期望抛出异常,可匹配消息expect(fn).toThrow("error")
expect(v).toBeNull()空值判断,另有 toBeUndefined/toBeDefinedexpect(value).toBeNull()
expect(v).toBeInstanceOf(C)实例类型断言expect(value).toBeInstanceOf(Class)
expect(obj).toMatchObject({ a: 1 })对象部分匹配,只比对指定字段expect(obj).toMatchObject({ a: 1 })
test("async", async () => {...})async/await 写法,Jest 自动等待test("async", async () => { await fn() })
() => fn().then(d => expect(d).toBe(1))返回 Promise 让 Jest 等待其落定test("promise", () => fn().then(d => expect(d).toBe(1)))
expect(p).resolves.toBe(v)断言 resolve 的值expect(promise).resolves.toBe(value)
expect(p).rejects.toThrow("err")断言 reject 的原因expect(promise).rejects.toThrow("err")
test("done", done => {...})done 回调写法,需手动调用test("done", done => { fn(done) })
jest.useFakeTimers()启用假定时器,替换真实计时jest.useFakeTimers()
jest.runAllTimers()把挂起的假定时器全部执行完jest.runAllTimers()
beforeEach / afterEach每个用例前后各执行一次beforeEach(() => setup())
beforeAll / afterAll所有用例前后各执行一次beforeAll(() => init())
jest.fn()创建 Mock 函数const fn = jest.fn()
jest.fn().mockReturnValue(42)设置固定返回值jest.fn().mockReturnValue(42)
jest.fn().mockResolvedValue(v)返回一个 resolve 的 Promisejest.fn().mockResolvedValue(val)
jest.fn().mockImplementation(fn)自定义 mock 的实现逻辑jest.fn().mockImplementation(n => n * 2)
jest.spyOn(obj, "method")spy 对象方法,默认保留原实现jest.spyOn(obj, "method")
jest.mock("./module")自动 mock 模块的所有导出jest.mock("./module")
jest.requireActual("./module")在 mock 上下文里取真实模块jest.requireActual("./module")
mockClear / mockReset / mockRestore清理调用记录、置空实现、恢复原方法fn.mockClear() / mockReset() / mockRestore()
toHaveBeenCalled()验证函数被调用过expect(fn).toHaveBeenCalled()
toHaveBeenCalledWith(arg)验证调用参数expect(fn).toHaveBeenCalledWith(arg)
toHaveBeenCalledTimes(2)验证调用次数expect(fn).toHaveBeenCalledTimes(2)
toHaveBeenLastCalledWith(arg)验证最后一次调用的参数expect(fn).toHaveBeenLastCalledWith(arg)
toMatchSnapshot()创建或对比快照expect(tree).toMatchSnapshot()
toMatchInlineSnapshot()内联快照,写入测试文件本身expect(obj).toMatchInlineSnapshot()
npx jest --coverage生成覆盖率报告npx jest --coverage
npx jest --watch监听改动只重跑相关测试npx jest --watch
npx jest --watchAll监听所有测试文件npx jest --watchAll
npx jest --bail首个失败立即停止npx jest --bail
npx jest -t "用例名"按用例名正则过滤运行npx jest -t "用例名"

易错点与避坑指南

现象用例总是通过,哪怕把断言里的值改成明显错误的,测试也不报错。

原因异步测试体没有 return 或 await,Jest 在 Promise 落定前就判定用例结束,断言根本没被执行。

处置把用例函数声明为 async 并对异步调用加 await,或直接 return 那个 Promise,让 Jest 正确等待。

现象单独跑某个用例通过,整套一起跑就失败,且失败点飘忽不定。

原因用例之间存在共享状态:上一个用例留下的 spy、mock 或全局变量没有清理。

处置在 afterEach 里调用 mockRestore() / mockClear() 恢复被替换的方法,或用 jest.resetModules() 清理模块缓存。

现象CI 上莫名其妙冒出一批新的失败快照,本地却是绿的。

原因代码改动导致的结构变化本该更新快照,但快照被连同旧值一起提交,CI 比对失败。

处置确认改动是有意的后本地跑一遍并用 -u 更新快照,把新快照一起提交;不要在 CI 里盲目加 -u。

现象提交后其他同事的测试大量莫名失败,怀疑自己改了公共配置。

原因调试时留下的 test.only 只运行被聚焦的用例,其余用例被静默跳过,合入主干后集中暴露。

处置提交前全局搜索 test.only 与 describe.only 确认已清除;也可以用 CI 参数确保跳过逻辑不生效。

现象定时器相关代码的测试永远不结束,跑到超时才报失败。

原因真实定时器需要真实时间等待,甚至配合网络请求导致测试挂起。

处置改用 jest.useFakeTimers() 替换真实计时器,再用 jest.runAllTimers() 同步推进,测试即可瞬时完成。

现象mock 掉整个模块后,被测代码拿到的全是空实现,测试报 undefined。

原因jest.mock 会把模块所有导出替换成 jest.fn(),默认返回 undefined。

处置用 mockReturnValue / mockResolvedValue 补上需要的返回值;若只要部分真实行为,用 jest.requireActual("./module") 混入真实实现。

现象用 toBe 比较两个内容相同的对象,断言失败。

原因toBe 是引用相等,对象每次构造都是新引用。

处置改用 toEqual 做深度比较;只关心部分字段时用 toMatchObject。

排障路径

  1. 1按用例名过滤快速定位失败测试

    npx jest -t "用例名"

    只运行名字匹配该正则的用例,避免整轮跑动浪费时间。

  2. 2首个失败即停,先看最关键的问题

    npx jest --bail

    一旦出现失败立即停止运行,输出指向第一个破坏链路的位置。

  3. 3生成覆盖率报告找出未测分支

    npx jest --coverage

    输出按文件列出语句与分支覆盖率,未覆盖行会直接列出。

  4. 4更新已确认变化的快照

    npx jest -u

    把过期的快照写入新值;提交前应人工核对 diff 确认变化是有意的。

  5. 5验证 mock 是否被正确调用

    expect(fn).toHaveBeenCalledWith(arg)

    失败信息会打印实际调用参数,可直接对照期望修正实现或断言。

  6. 6同步推进假定时器让异步用例不超时

    jest.useFakeTimers(); jest.runAllTimers()

    两个调用配合使用,把挂起的定时器回调全部执行完再断言。

命令示例

异步测试:return / await / resolves 三种等价写法

test("resolves", () => save().then((d) => expect(d).toBe(1))); test("async/await", async () => { const d = await save(); expect(d).toBe(1); }); test("resolves matcher", () => { expect(save()).resolves.toBe(1); expect(save()).rejects.toThrow("failed"); });

漏掉 await/return 时 Jest 会在 Promise 完成前就判定通过,断言根本没执行——失败测试却显示绿色。用 async + await 写最直观;只断言单个值时 resolves/rejects 更短。

Mock:固定返回值、异步返回值与调用断言

const send = jest.fn(); send.mockResolvedValue({ ok: true }); await handler(send); expect(send).toHaveBeenCalled(); expect(send).toHaveBeenCalledTimes(2); expect(send).toHaveBeenLastCalledWith({ id: 7 }); send.mockClear();

只断言「被调用过」无法区分调用了几次、参数对不对;toHaveBeenCalledTimes 与 toHaveBeenLastCalledWith 才是真正拦住回归的两道。测试之间复用同一个 mock 时必须 mockClear,否则计数会跨用例累加。

部分 mock:spyOn 保留原实现,requireActual 取真实模块

const spy = jest.spyOn(api, "fetch"); spy.mockResolvedValue({ data: [] }); // 需要其余导出仍是真实实现时 const real = jest.requireActual("./module"); afterEach(() => { spy.mockRestore(); });

jest.mock("./module") 会把整个模块的导出全换成空实现,连你没想替换的工具函数也变 undefined。只替换一个方法时用 spyOn,并在 afterEach 里 mockRestore,否则它会泄漏到后续所有测试。

假定时器:让 debounce/重试逻辑瞬间跑完

jest.useFakeTimers(); test("debounce fires once", () => { const spy = jest.fn(); debounce(spy, 300); debounce(spy, 300); jest.runAllTimers(); expect(spy).toHaveBeenCalledTimes(1); });

不用假定时器就只能真的等 300ms,测试既慢又不稳(机器慢一点就偶发失败)。启用后 runAllTimers 把待执行的定时器一次跑完;但记得测试结束后恢复真实定时器,否则后续用例里的真实定时逻辑全部挂起。

生命周期钩子把 setup/cleanup 收成一处

beforeAll(() => (server = startServer())); beforeEach(() => setup()); afterEach(() => cleanup()); afterAll(() => teardown());

把初始化/销毁放在钩子里而不是复制进每个 it,能避免「新加一个测试忘了清理」这类漏网——漏了清理往往不报错,而是污染下一个用例,出现只在单跑时通过的幽灵 bug。

常见坑与注意事项

  • 漏写 await 或 return 时 Jest 会在 Promise 完成前结束用例,断言从未执行却显示通过——异步逻辑的失败被静默吞掉,CI 上极难发现。
  • jest.fn() / jest.spyOn 生成的 mock 不自动清理,复用同一个 mock 会让 toHaveBeenCalledTimes 跨用例累加;需在 afterEach 里 mockClear 或 mockReset。
  • jest.spyOn 只在当前用例里替身,若忘记 mockRestore,替换会持续影响同一 worker 内后续所有测试文件里对该模块的引用,报错指向毫不相干的用例。
  • toMatchSnapshot 的快照一旦过期就该显式用 -u 更新;为了让测试变绿而无脑更新快照,会把真实的行为变更一起固化进去。
  • useFakeTimers 之后未恢复真实定时器,后续用例中依赖 setTimeout / setInterval 的代码(含第三方库初始化)会永久挂起,表现为整片用例超时。
  • test.only 提交前忘记删除,会让 CI 只跑这一个用例,其余测试全部不执行——绿色流水线不代表代码没问题。

提示

  • jest --coverage 生成覆盖率报告,--watch 开启监听模式。
  • jest.mock() 会自动 Mock 模块的所有导出,返回 jest.fn()。
  • toMatchSnapshot() 适合 UI 组件和序列化结构测试,更新快照加 -u 参数。
  • jest.spyOn(obj, "method") 可以 spy 对象方法而不完全 Mock。
  • useFakeTimers() 配合 runAllTimers() 可同步测试定时器相关逻辑。
  • test.only 调试完记得删除,否则会跳过其他测试用例。

常见问题

异步测试写完却总是立刻通过,漏掉了失败?

这是最常见的假阳性:测试体既没写 async 也没 return Promise,Jest 认为用例同步结束直接判过,Promise 的 rejection 变成了无人处理的错误。正确写法有两种:一是 test("async", async () => { await fn() }),二是 test("promise", () => fn().then(d => expect(d).toBe(1))),显式 return 让 Jest 等待。第三种是 done 回调写法 test("done", done => { fn(done) }),必须手动调用 done,漏调会超时。断言异步结果更推荐 expect(promise).resolves.toBe(value) 和 expect(promise).rejects.toThrow("err"),失败信息比在 then 里写 expect 清楚得多。

mock 出来的函数怎么验证「调用方式和次数」?

jest.fn() 创建的 Mock 函数会记录每次调用,可以直接断言三件事:expect(fn).toHaveBeenCalled() 验证至少被调用过;expect(fn).toHaveBeenCalledWith(arg) 验证某次调用带了指定参数;expect(fn).toHaveBeenCalledTimes(2) 验证调用次数,需要精确计数时优先用它;expect(fn).toHaveBeenLastCalledWith(arg) 只看最后一次调用的参数。返回值侧用 mockReturnValue(42)、mockResolvedValue(val)、mockImplementation(n => n * 2) 配置。断言之间记得用 mockClear() 清调用记录、mockReset() 连实现一起清、mockRestore() 还原被 spy 的原方法,避免测试间串味。

定时器逻辑为什么跑不过去,改成假定时器就行了吗?

setTimeout、setInterval 这类异步等待会让测试变慢甚至挂住。jest.useFakeTimers() 把这些定时器替换成可手动推进的假实现,之后必须主动调用 jest.runAllTimers() 把所有待执行的回调一次性跑完(要指定挂起时长可配合 jest.advanceTimersByTime(ms)),否则断言会在定时器触发之前就执行从而失败。两个前提:假定时器应在测试开始前启用,并记得在测试结束后恢复,否则后续依赖真实计时的用例会全部卡住;对第三方库中的定时器,fake timers 是否生效取决于该库是否使用全局的 setTimeout 而非自己保存的引用。

jest.mock 和 jest.spyOn 各自什么时候用?

jest.mock("./module") 作用于整个模块:所有对该模块的导入都被替换成自动 Mock,返回值是 jest.fn(),测试里再 mockReturnValue 设定行为。适合把外部依赖整体隔离出去。要在 Mock 上下文里访问真实实现,用 jest.requireActual("./module")。jest.spyOn(obj, "method") 则是局部手段,默认保留原实现,你可以在调用后再通过 expect(spy).toHaveBeenCalled() 观察调用,同时选择继续走真逻辑或用 mockImplementation 覆盖。经验法则是:需要验证「有没有被调用」用 spyOn,需要彻底切断依赖行为用 jest.mock;若用了 mockReturnValue 之外的方式还需清理,见 mockRestore。

快照测试什么时候值得用,怎么更新快照?

toMatchSnapshot() 把序列化结果首次运行时写成 .snap 文件,之后每次比对;UI 组件和大段序列化结构用它最省事,因为逐个属性写断言成本高且容易漏。更新用 -u 参数:npx jest -u 重新写入已变化的快照;加到 test.only 上配合 npx jest -t "用例名" 可以先聚焦单个用例再更新,范围可控。风险在于快照可能掩盖回归,所以快照要人工 review 差异再提交。想让快照跟着代码走就用 toMatchInlineSnapshot(),结果直接写回测试文件,评审 diff 时能立刻看出结构变化。

官方参考来源

下方为命令对应的官方权威文档,供你核对最新用法与深入查阅。

由 巧匠 维护

公开更新于 2026年7月21日,内容持续校对官方文档。

联系我们

命令或描述有误?提交反馈、商务合作或产品建议都可发送邮件给我们。

联系我们