JavaScript单元测试中Mock动态导入需绕过实际加载,推荐用jest.unstable_mockModule(Jest≥29.5)mock固定路径模块,或封装import()为可注入函数便于替换,注意变量路径无法被jest.mock()拦截。

在 JavaScript 单元测试中 Mock 动态导入(import())需要绕过实际的模块加载,因为动态导入返回的是 Promise,且模块路径常为变量或运行时拼接,不能像静态 import 那样用常规的 jest.mock() 直接处理。
方案一:Mock import() 全局函数(推荐)
Jest 允许你直接 mock 全局的 import 函数(注意不是 require),适用于大多数场景。关键点是:必须在测试文件顶部(或 beforeAll 中)执行 mock,并确保它在被测代码调用前生效。
- 使用
jest.unstable_mockModule(Jest ≥29.5)更安全、更语义化,但需配合await import()的异步加载逻辑 - 若用旧版 Jest 或需兼容性,可直接覆盖全局
import:
注意:仅限 Node.js 环境;浏览器环境不可行(import是语法关键字,无法重赋值)
示例(Jest ≥29.5):
// myComponent.test.js
beforeEach(async () => {
// Mock 模块行为:当 import('./utils.js') 被调用时,返回一个带 mock 方法的对象
await jest.unstable_mockModule('./utils.js', () => ({
fetchData: jest.fn().mockResolvedValue({ ok: true }),
}));
});
test('calls dynamic import and uses result', async () => {
const { loadAndUse } = await import('./myComponent.js');
await loadAndUse();
// 断言 utils.fetchData 被调用
const utils = await import('./utils.js');
expect(utils.fetchData).toHaveBeenCalled();
});
方案二:在被测代码中抽象动态导入逻辑
将 import() 封装成可注入的函数,便于测试时替换。这是更可控、更易维护的方式。
- 把
import()调用抽离为参数或依赖项(如工厂函数、选项对象) - 测试时传入返回 mock 数据的 Promise
示例:
// myComponent.js
export async function loadAndUse(importFn = () => import('./utils.js')) {
const { fetchData } = await importFn();
return fetchData();
}
// myComponent.test.js
test('uses injected import function', async () => {
const mockFetchData = jest.fn().mockResolvedValue('mocked');
const importFn = jest.fn().mockResolvedValue({ fetchData: mockFetchData });
await loadAndUse(importFn);
expect(importFn).toHaveBeenCalled();
expect(mockFetchData).toHaveBeenCalled();
});
方案三:Mock 特定路径的模块(适用于已知固定路径)
如果动态导入的路径是确定的字符串(例如 import('./config.js')),可用 jest.mock() + jest.requireActual() 组合实现“自动 mock”。
- 在
jest.mock('./config.js')后,Jest 会拦截所有对该路径的import()调用(包括动态导入) - 需手动指定 mock 返回值,通常是一个 resolve 某个对象的 Promise
示例:
// myComponent.test.js
jest.mock('./config.js', () => ({
API_URL: 'https://test.example.com',
}));
test('loads config dynamically', async () => {
const { getConfig } = await import('./myComponent.js');
const config = await getConfig(); // 内部执行 import('./config.js')
expect(config.API_URL).toBe('https://test.example.com');
});
注意事项与避坑点
-
jest.mock()对动态导入的拦截只对**字面量字符串路径**有效(如import('./a.js')),不支持变量拼接(如import(`./${name}.js`)) - Node.js 中全局
import是只读绑定,不能直接赋值;但 Jest 在 test context 中允许通过jest.unstable_mockModule或jest.mock()实现等效效果 - 避免在测试中混用
require()和import()加载同一模块,可能导致模块缓存不一致 - ESM 测试环境(
"type": "module")下,确保 Jest 配置启用 ESM 支持(如extensionsToTreatAsEsm: ['.js']和globals: { 'ts-jest': { useESM: true } })











