
vue/vitest 项目中常遇到这类问题:vitest 无法直接 mock 同一模块内的内部函数调用(如 `parent` 调用同模块的 `child`),因其导入绑定在模块初始化时已固化;解决方案是将被测副作用函数拆分到独立模块,再通过 `vi.mock()` 精确替换。
在单元测试中,模拟(mock)副作用函数(如日志、API 调用、随机生成等)是隔离依赖、保证测试确定性的关键。但使用 Vitest 时,一个常见误区是试图直接 vi.spyOn() 或 vi.mock() 当前模块内定义并被同模块其他函数调用的函数——这在 ES 模块环境下必然失败。原因在于:ESM 的导入导出是静态绑定且不可变的,parent() 内部对 child() 的引用指向的是模块作用域内的原始函数,而非后续通过 spyOn 创建的代理。
✅ 正确做法是遵循「依赖可注入」原则,将副作用逻辑抽离为独立模块:
// dummy-child.ts
export function child(): string {
console.log('calling actual child');
return 'bar';
}// dummy-parent.ts
import { child } from './dummy-child';
export function parent(): string {
return `foo${child()}`;
}随后在测试中,使用 vi.mock() 在导入 dummy-parent 之前,为 dummy-child 提供模拟实现:
动态WEB网站中的PHP和MySQL详细反映实际程序的需求,仔细地探讨外部数据的验证(例如信用卡卡号的格式)、用户登录以及如何使用模板建立网页的标准外观。动态WEB网站中的PHP和MySQL的内容不仅仅是这些。书中还提到如何串联JavaScript与PHP让用户操作时更快、更方便。还有正确处理用户输入错误的方法,让网站看起来更专业。另外还引入大量来自PEAR外挂函数库的强大功能,对常用的、强大的包
// dummy.test.ts
import { parent } from './dummy-parent';
// ✅ 在 import 之后、测试前 mock 依赖模块
vi.mock('./dummy-child', () => ({
child: () => 'baz',
}));
describe('parent', () => {
it('should return foobaz when child is mocked', () => {
expect(parent()).toBe('foobaz'); // ✅ 通过
});
});⚠️ 关键注意事项:
- vi.mock() 必须置于顶层作用域(不能在 describe 或 it 内部),且需在目标模块被 import 之后、首次使用前执行;
- 若使用 vi.mock('./dummy-child', async (importActual) => {...}) 形式,记得 await importActual() 并显式覆盖所需导出;
- 不要尝试 vi.spyOn(module, 'fn') 来 mock 同模块内函数调用——它只影响通过该引用调用的场景(如 module.child()),对模块内部直调无效;
- 对于无法重构的遗留代码,可考虑使用 vi.hoisted + vi.mock 动态重写,但应视为临时方案。
总结:Vitest 的模块模拟机制基于 ESM 的静态导入图,因此「解耦副作用」是可靠 mock 的前提。将 child 提取为独立模块,不仅使测试可行,也提升了代码的可维护性与关注点分离度。









