
本文介绍在 Vitest 中为每个测试独立 mock JSON 文件导入的正确方法,解决因模块缓存导致 mock 失效的问题,核心是结合 vi.doMock()、动态 import() 和 vi.resetModules() 实现精准、隔离的测试控制。
本文介绍在 vitest 中为每个测试独立 mock json 文件导入的正确方法,解决因模块缓存导致 mock 失效的问题,核心是结合 `vi.domock()`、动态 `import()` 和 `vi.resetmodules()` 实现精准、隔离的测试控制。
在使用 Vitest 编写单元测试时,若待测函数(如 validateData())直接静态导入了 JSON 文件(例如 import data from 'data/data.json'),默认情况下该模块会在首次加载时被缓存。这意味着:即使你在测试中调用 vi.mock() 或修改 mock 返回值,后续测试仍会复用最初解析的 JSON 内容——mock 无法“重生效”,导致测试相互污染、结果不可靠。
正确的解决方案是打破模块缓存链路,让每次测试都重新解析并应用新的 mock。这需要三个关键操作协同:
-
使用
vi.doMock()替代vi.mock():doMock()是运行时指令,仅对后续的import()生效,且不会被提升(hoist),因此可灵活按需配置; -
配合动态
import():通过await import('./validator.js')显式触发模块重新加载,确保当前 test 中的doMock配置被实际应用; -
在
afterEach中调用vi.resetModules():清除 Node.js 模块缓存(require.cache),防止跨测试残留,保障完全隔离。
以下是推荐的完整实践代码:
const obj1 = {
id: 1,
name: 'obj1'
};
describe('validateData', () => {
afterEach(() => {
vi.resetModules(); // ✅ 清除模块缓存,避免污染下一个 test
});
test('with valid data', async () => {
vi.doMock('data/data.json', () => ({
default: { obj1 } // ✅ 本次 test 的专属 mock 数据
}));
const { validateData } = await import('./validator.js'); // ✅ 触发重加载
expect(validateData()).toStrictEqual([]);
});
test('with missing field', async () => {
vi.doMock('data/data.json', () => ({
default: { obj1: { ...obj1, name: undefined } } // ✅ 不同结构的 mock
}));
const { validateData } = await import('./validator.js');
expect(validateData()).toStrictEqual(['Missing name for obj1']);
});
});⚠️ 注意事项:
- 不要提前静态导入
validateData或 JSON 文件(如顶部import { validateData } from './validator.js'),否则将绕过doMock机制; -
vi.doMock()必须在import()调用前执行,顺序不可颠倒; - 若项目启用 ESM 且 JSON 导入需额外配置(如 Vite 的
json插件),请确保测试环境与构建环境一致; - 对于 TypeScript 用户,建议在
vitest.config.ts中启用esbuild的json支持或使用@rollup/plugin-json等兼容方案。
通过该模式,每个测试都拥有干净的模块上下文和定制化的 JSON 响应,真正实现“一测一 mock”,大幅提升验证逻辑的准确性和可维护性。


















