模拟(Mock)
基于 Jest v30.x 编写
速查
- 造函数:
jest.fn(),.mockReturnValue/.mockResolvedValue/*Once;.mock.calls看调用 - 监视:
jest.spyOn(obj, "m"),默认仍调原实现;jest.restoreAllMocks()恢复;Jest 30 可用using自动恢复 - 模块 mock:
jest.mock(path, factory?)——Babel 提升到 import 之前(CJS) - 取原始:
jest.requireActual(path)——同步(Vitest 的vi.importActual是异步) - 手动 mock:
__mocks__/目录——node_modules 旁的自动加载,用户模块旁需显式jest.mock - 假定时器:
jest.useFakeTimers()+runAllTimers/advanceTimersByTime
jest.fn
ts
const fn = jest.fn().mockReturnValue("Hi");
fn("a", 1);
expect(fn).toHaveBeenCalledWith("a", 1);
expect(fn.mock.calls).toEqual([["a", 1]]); // 所有调用的参数
expect(fn.mock.lastCall).toEqual(["a", 1]);
// 链式控制
const m = jest
.fn()
.mockReturnValueOnce("first")
.mockReturnValue("default")
.mockResolvedValue({ data: [] });.mock 上还有 results(返回值)、instances、contexts 等。
jest.spyOn
ts
const spy = jest.spyOn(video, "play"); // 默认仍调用原实现
video.play();
expect(spy).toHaveBeenCalled();
jest.spyOn(api, "fetch").mockResolvedValue({ data: [] }); // 覆盖实现
afterEach(() => jest.restoreAllMocks());Jest 30 支持 using 自动恢复(TS 5.2+ / Babel 插件):
ts
test("自动恢复", () => {
using spy = jest.spyOn(console, "warn");
doWarn();
expect(spy).toHaveBeenCalled();
}); // 块结束自动 restore,无需 afterEachjest.mock 与提升
jest.mock 被 Babel 插件提升到文件顶部,在 import 之前执行(CJS):
ts
import axios from "axios"; // 实际拿到 mock 后的版本
jest.mock("axios"); // automock:所有方法变 jest.fn()
test("", async () => {
axios.get.mockResolvedValue({ data: [{ name: "Bob" }] });
// ...
});ESM 下不提升
ESM 环境 jest.mock 的提升不生效,要改用 jest.unstable_mockModule + 动态 import(),详见 ESM 与对照 Vitest。
jest.requireActual(同步)
保留部分原实现时取原始模块——Jest 是同步的,这与 Vitest 的异步 vi.importActual 是关键差异:
ts
jest.mock("../myModule", () => {
const original = jest.requireActual("../myModule"); // 同步,无需 await
return {
__esModule: true,
...original,
getRandom: jest.fn(() => 10), // 只覆盖这一个
};
});mocks 目录(手动 mock)
project/
├── __mocks__/
│ ├── lodash.js # node_modules 包 → 自动加载(无需 jest.mock)
│ └── fs.js # Node 内置 → 仍需显式 jest.mock("fs")
└── models/
├── __mocks__/
│ └── user.js # 用户模块 → 需显式 jest.mock("./user")
└── user.js加载规则(与 Vitest 的关键差异)
- node_modules 旁的
__mocks__:第三方包自动 mock,无需调用jest.mock("lodash") - 用户模块旁的
__mocks__:仍需jest.mock("./user")显式启用 - Node 内置模块(fs / path):即便有 mock 文件也需显式
jest.mock("fs") - Vitest 无此自动加载机制,全部需显式
vi.mock
js
// __mocks__/fs.js
const fs = jest.createMockFromModule("fs");
module.exports = fs;automock
ts
// jest.config.ts → automock: true,所有模块自动 mock
jest.unmock("../realModule"); // 选择性取消假定时器
ts
jest.useFakeTimers();
test("1 秒后触发", () => {
const cb = jest.fn();
setTimeout(cb, 1000);
jest.advanceTimersByTime(1000); // 精确推进(推荐)
// 或 jest.runAllTimers() / jest.runOnlyPendingTimers()
expect(cb).toHaveBeenCalledTimes(1);
});
jest.useFakeTimers({
now: new Date("2026-01-01"), // 固定初始时间
doNotFake: ["performance"], // 排除某些 API
});
jest.advanceTimersToNextFrame(); // Jest 30:推进到下一 rAF 帧(约 16ms)
jest.useRealTimers(); // 恢复清理策略(clearMocks / resetMocks / restoreMocks)见 配置。