vitest 中 vi.mock 用于模块级依赖替换,vi.spyon 用于运行时函数行为劫持;前者需顶层声明、仅对 es 模块生效,后者须作用于可访问对象属性并注意清理。

在 Vitest 中高效模拟,关键不是“用哪个 API”,而是“在什么场景下用对的工具”。vi.mock 和 vi.spyOn 解决的是两类不同层级的问题:一个是模块级依赖替换,一个是运行时函数行为劫持。用错地方,测试就容易失效或难以维护。
vi.mock:用于替换整个模块的导入行为
当你需要让某个被测模块(比如 parent.ts)在加载时,把它的 import { child } from './child' 自动指向你定义的假实现,就必须用 vi.mock()。它在模块解析阶段介入,确保所有 import 都命中 mock 版本。
- 必须写在文件顶层,且在目标模块 import 之后、测试之前(推荐紧接 imports 下方)
- 只对 ES 模块(import/export)生效;若项目混用 require,vi.mock 会静默失效
- 工厂函数返回的对象,会完全覆盖原模块导出 —— 不用再手动 mockReturnValue,直接返回值或 mock 函数都行
- 适合模拟 API 调用、工具库、副作用函数等外部依赖
示例:
// api.tsexport async function fetchUser(id) {
const res = await fetch(`/users/${id}`);
return res.json();
}
// api.test.ts
import { fetchUser } from './api';
vi.mock('./api', async (importActual) => {
const actual = await importActual();
return {
...actual,
fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
};
});
it('returns mocked user', async () => {
expect(await fetchUser(1)).toEqual({ id: 1, name: 'Alice' });
expect(fetchUser).toBeCalledTimes(1);
});
vi.spyOn:用于观测或临时修改已有对象上的方法
vi.spyOn 不是“模拟模块”,而是“监听一个已存在引用的方法”。它适合验证某函数是否被调用、传了什么参数、调用了几次,或者临时改掉某个对象身上的方法(比如全局 console、第三方实例方法)。
- 作用对象必须是可访问的变量/属性,例如 console.log、Math.random、instance.method
- 对模块内部直调(如 parent() 内部直接调 child())无效 —— 因为那不是通过对象属性访问的
- 记得用 .mockRestore() 清理,否则可能污染其他测试
- 常用于打桩全局副作用、校验日志、拦截类方法调用
示例:
it('logs warning when input is empty', () => {const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
validateInput('');
expect(warnSpy).toBeCalledWith('Input cannot be empty');
warnSpy.mockRestore();
});
别踩坑:常见误用与修复方式
很多失败不是 API 不好用,而是没理解 ESM 的静态绑定本质。
- 想 mock 同一文件里的函数?不行 —— ESM 不允许重绑定本地变量。解法:把该函数拆到独立模块,再用 vi.mock 替换
- vi.mock 写在 describe 或 it 里?不生效 —— 它必须在模块加载前注册,只能放在顶层
- 测试中用了 require 导入?vi.mock 失效 —— 统一改为 import,这是现代 Vitest 测试的前提
- mock 了但函数还是执行了?检查是否真的 import 了那个模块 —— 有些逻辑可能是动态 require 或 eval,这些无法被 vi.mock 拦截
组合使用更强大
真实场景往往需要两者配合。比如测试一个组件方法,既要 spy 它调用的内部状态更新函数,又要 mock 它依赖的 API 模块:
- 用 vi.mock 替换 API 模块,控制返回数据
- 用 vi.spyOn 监听组件实例方法或全局副作用,验证触发时机和参数
- 测试结束后统一调用 vi.clearAllMocks() 快速重置所有 mock 状态











