<p>“有选择地 Mock 全部导出模块”指仅 mock 部分导出项而保留其余真实行为,即部分 Mock(Partial Mock);ESM 中推荐 import * as + jest.spyOn(),CommonJS 中可直接修改 require 缓存对象,Jest 内联 mock 用 jest.mock() 工厂函数透传实际模块,无框架时可用 Proxy 动态拦截。</p>

在 JS 模块化开发中,“有选择地 Mock 全部导出模块”实际指的不是 mock 整个模块,而是**只 mock 某些导出项,其余保持真实行为**——即“部分 Mock(Partial Mock)”。这在测试中非常常见:比如只想拦截 fetchUser,但让 fetchPost 和工具函数照常运行。
ESM 环境下推荐:用 import * as + jest.spyOn()
ES 模块的导出是只读绑定,不能直接赋值覆盖。最稳妥的方式是:
- 在测试文件顶部用
import * as moduleNs from './xxx'导入命名空间对象 - 在
beforeEach中调用jest.spyOn(moduleNs, 'functionName')并设置返回值 - 异步函数必须用
.mockResolvedValue()(不是.mockReturnValue()) - 测试结束后调用
jest.restoreAllMocks()防止污染其他测试
✅ 示例:
test/user.test.js
import * as api from '../api';
import { getUserProfile } from '../service';
<p>beforeEach(() => {
jest.spyOn(api, 'fetchUser').mockResolvedValue({ id: 1, name: 'Mocked' });
});</p><p>afterEach(() => {
jest.restoreAllMocks();
});</p><p>test('uses mocked fetchUser', async () => {
const result = await getUserProfile(1);
expect(result.name).toBe('Mocked');
});
CommonJS 环境下:直接修改 require 返回的对象
Node.js 或老项目若用 require 加载模块,可利用其缓存机制做轻量替换:
- 先
const original = require('./service')获取原始导出对象 - 用
original.method = jest.fn().mockReturnValue(...)替换目标方法 - 确保被测代码
require的是同一路径(Node 缓存保证单例) - 注意:模块导出必须是可变对象(不能是
Object.freeze()后的)
Jest 内联 mock:用 jest.mock() 工厂函数委托真实逻辑
适用于需在模块加载前统一控制的场景(如多个测试共用同一 partial mock):
- 在
import语句前调用jest.mock('./api', () => { ... }) - 工厂函数内用
jest.requireActual('./api')获取原始模块 - 返回对象中只覆盖要 mock 的属性,其余属性全部透传
- 路径必须与
import完全一致(含扩展名)
✅ 示例:
// 必须写在 import 之前
jest.mock('./api.js', () => {
const actual = jest.requireActual('./api.js');
return {
...actual, // 保留所有原始导出
fetchUser: jest.fn().mockResolvedValue({ id: 999, name: 'Stubbed' }),
};
});
<p>import { fetchUser, fetchPost } from './api.js'; // 此时 fetchUser 已被替换,fetchPost 仍真实
不依赖测试框架时:用 Proxy 包装原始模块
适合浏览器环境、自研测试工具或需要运行时动态控制的场景:
- 导入原始模块:
const real = require('./logger')或await import('./logger') - 用
new Proxy(real, { get() { ... } })拦截特定属性访问 - 匹配到目标方法名时返回 mock 实现;否则返回原属性
- 完全绕过 Jest,也无需改模块导出方式
✅ 示例:
const realLogger = await import('./logger');
const logger = new Proxy(realLogger, {
get(target, prop) {
if (prop === 'error') {
return (...args) => console.warn('[MOCKED]', ...args);
}
return target[prop];
}
});
// 后续使用 logger.info / logger.error 均走此代理


















