动态导入需用 jest.mock() 提前模拟路径并返回 Promise 结果,路径须精确含扩展名;可 mock 全局 import 函数或注入 import 依赖提升可测性;ESM 模拟需匹配 default 或具名导出结构。

动态导入(import())在测试中不能用常规 jest.mock() 顶层调用直接拦截,因为它是运行时行为、不参与静态分析。要 Mock 动态导入的模块,关键在于**控制其返回的 Promise 结果**,而不是试图“替换模块加载器”。
提前 mock 动态导入的模块路径
在测试文件顶部,对目标模块路径显式调用 jest.mock(),即使它只被 import() 调用。Jest 会将该路径纳入模拟系统,后续所有对该路径的 import() 都会返回你定义的模拟对象:
- 路径必须完全一致(含扩展名,如
'./utils.js',不能省略.js) -
jest.mock()必须写在任何import或require之前(包括测试工具库) - 推荐搭配
jest.requireActual()保留未 mock 的导出
示例:
jest.mock('./heavyModule.js', () => ({
doWork: jest.fn().mockResolvedValue('mock result'),
// 其他导出可留空或按需模拟
}));
<p>import { lazyLoad } from './app.js';</p><p>test('loads and uses dynamic module', async () => {
const result = await lazyLoad(); // 内部执行 import('./heavyModule.js')
expect(result).toBe('mock result');
});
Mock import() 函数本身(适用于通用拦截)
如果需要统一拦截所有 import() 调用(比如全局降级逻辑),可直接 mock 全局 import:
- 仅限 Node.js 环境(Jest 默认环境),浏览器中不可覆盖原生
import - 需在
beforeAll中设置,并注意还原避免污染其他测试 - 适合模拟加载失败、延迟、或按路径返回不同模块
示例:
beforeAll(() => {
jest.unstable_mockModule('module', () => ({
default: { value: 'from mocked import' }
}));
});
<p>// 或更底层:mock import 函数(Node v18.17+ / Jest 29.7+)
global.import = jest.fn((specifier) => {
if (specifier === './feature-a.js') {
return Promise.resolve({ init: jest.fn() });
}
return Promise.resolve({ fallback: true });
});
在被测代码中注入 import 函数(推荐,解耦更强)
把 import 当作依赖传入,而非硬编码调用。这样测试时可直接传入返回 mock 模块的函数:
- 提升可测试性,也利于按需加载策略切换(如 SSR 中禁用动态导入)
- 避免全局副作用,无需操作
global.import - 符合依赖注入原则,逻辑更清晰
被测代码写法:
// app.js
export const loadFeature = async (importFn = import) => {
const { render } = await importFn('./feature.js');
return render();
};
测试写法:
test('uses injected import', async () => {
const mockImport = jest.fn().mockResolvedValue({
render: jest.fn().mockReturnValue('mock UI')
});
<p>const result = await loadFeature(mockImport);</p><p>expect(result).toBe('mock UI');
expect(mockImport).toHaveBeenCalledWith('./feature.js');
});
处理 ESM 动态导入与默认导出问题
ESM 中 import('./x').then(m => m.default) 常见,但 mock 返回的对象若没提供 default 属性,会报错。务必确保模拟对象结构匹配运行时预期:
- 如果原始模块是
export default xxx,mock 对象必须有default字段 - 如果原始模块是
export const fn = ...,mock 对象应包含同名属性,或使用import * as mod方式导入后 spyOn - 不确定结构时,可用
jest.requireActual()+ 手动赋值补全
安全写法:
jest.mock('./dialog.js', () => {
const actual = jest.requireActual('./dialog.js');
return {
...actual,
default: jest.fn().mockImplementation(() => 'mock dialog'),
showDialog: jest.fn().mockResolvedValue(true)
};
});


















