vi.mock 是 Vitest 中用于局部模块模拟的 API,必须在顶层调用,支持按需覆盖导出、混合导出模拟及通过 vi.unmock 等清理策略管理 mock 状态。

vi.mock 是 Vitest 提供的模块模拟 API,用于在测试中局部替换(即“打桩”)指定模块的导出,**不影响其他测试文件或同一文件中未被 mock 的部分**。它不是全局替换,而是作用于当前测试文件(或 describe 块内),且需配合 vi.unmock 或重置策略谨慎使用。
必须在顶层调用,且不能在条件逻辑中
和 Jest 的 jest.mock 类似,vi.mock 必须出现在测试文件的最外层作用域(即 module scope),不能包裹在 test、describe 或 if 语句里,否则会被忽略或报错:
test('should call api', () => {
vi.mock('./api'); // ❌ 运行时无效,Vitest 会警告
});✔ 正确写法:
// ✅ 顶层调用,Vitest 在编译阶段静态分析并注入 mock
vi.mock('./utils', () => ({
formatName: (name) => `Mocked: ${name}`,
DEFAULT_LANG: 'zh-CN',
}));
<p>import { formatName, DEFAULT_LANG } from './utils';</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/c1c2c2ed740f" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">Java免费学习笔记(深入)</a></a>”;</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill3430" title="Alibabacloud Sdk Client Initialization For Java"><img
src="https://img.php.cn/upload/skill/000/000/081/178955835420587.jpg" alt="Alibabacloud Sdk Client Initialization For Java" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill3430" title="Alibabacloud Sdk Client Initialization For Java">Alibabacloud Sdk Client Initialization For Java</a>
<p>在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。</p>
</div>
<a href="/xiazai/skill3430" title="Alibabacloud Sdk Client Initialization For Java" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><p>test('uses mocked formatName', () => {
expect(formatName('alice')).toBe('Mocked: alice');
expect(DEFAULT_LANG).toBe('zh-CN');
});按需 mock 部分导出(推荐:factory 函数 + 实际导入)
若只想替换某个函数,保留其余导出,可先 import 原模块,再在 factory 中选择性覆盖:
import { getTimestamp, log } from './helpers';
<p>// 只 mock getTimestamp,log 仍为真实实现
vi.mock('./helpers', async (importOriginal) => {
const actual = await importOriginal();
return {
...actual, // 保留所有原有导出
getTimestamp: () => 1234567890, // 仅覆盖这一项
};
});</p><p>test('getTimestamp is mocked', () => {
expect(getTimestamp()).toBe(1234567890);
expect(log('test')).toBeUndefined(); // log 仍是真实函数(假设它不依赖 getTimestamp)
});模拟默认导出与命名导出混合的模块
当模块同时有 export default 和 export const xxx 时,factory 返回对象需显式包含 default 字段:
// ./logger.js
export default class Logger {
log(msg) { console.log(msg); }
}
export const LEVEL = 'INFO';
<p>// 测试文件中
vi.mock('./logger', () => ({
default: class MockLogger {
log(msg) { /<em> 自定义行为 </em>/ }
},
LEVEL: 'TEST',
}));</p><p>import Logger, { LEVEL } from './logger';</p><p>test('mocks both default and named exports', () => {
const logger = new Logger();
expect(logger instanceof Logger).toBe(true); // 注意:此时构造的是 MockLogger
expect(LEVEL).toBe('TEST');
});清除 mock 并恢复原始行为(按需)
如果同一文件中多个测试需要不同行为,可用 vi.unmock 清除,或用 vi.restoreAllMocks() 重置所有 mock 状态:
-
vi.unmock('./module'):移除对该模块的 mock,后续 import 将加载真实模块 -
vi.resetAllMocks():重置所有 mock 函数的调用记录(但不还原模块本身) -
vi.restoreAllMocks():还原所有 mock 函数为原始实现(适用于vi.fn()创建的 mock)
注意:vi.mock 本身是静态声明,vi.unmock 不会撤销已发生的模块替换,它主要影响后续的 import 行为 —— 更稳妥的做法是在独立的 describe 块中隔离不同 mock 场景。

















