如何使用proxyquire模拟模块函数?重写mkdirp-promise Mock遇阻求助
我之前也碰到过一模一样的问题!用proxyquire模拟模块时,第一个测试用例的Mock会“粘”在后续测试里,本质是Node.js的模块缓存机制和proxyquire的加载逻辑在搞鬼。咱们一步步来解决:
核心原因
Node.js会缓存已加载的模块,proxyquire如果不是每次测试都重新加载目标模块,就会复用第一次加载时绑定的Mock。另外,如果你的Mock是全局复用的单一实例(比如在describe外层定义的jest.fn()),后续修改它的行为可能不会生效,因为模块已经绑定了旧的Mock引用。
解决方案:每次测试都“刷新”Mock和模块加载
这里以Jest为例,给出两种可行的写法:
写法1:用beforeEach重置Mock并重新加载模块
const proxyquire = require('proxyquire'); describe('你的目标模块测试', () => { let mkdirpPromiseMock; let yourTargetModule; // 每个测试用例执行前,重新创建Mock并加载模块 beforeEach(() => { // 1. 创建全新的Mock函数 mkdirpPromiseMock = jest.fn(); // 2. 用proxyquire重新加载目标模块,绑定新的Mock yourTargetModule = proxyquire('../path/to/your-target-module', { 'mkdirp-promise': mkdirpPromiseMock }); // 3. 清除目标模块的Node.js缓存(可选,但能彻底避免缓存问题) delete require.cache[require.resolve('../path/to/your-target-module')]; }); it('第一个测试:验证成功场景', async () => { // 给当前测试设置Mock行为 mkdirpPromiseMock.mockResolvedValue(true); await yourTargetModule.yourFunction('/test/path'); expect(mkdirpPromiseMock).toHaveBeenCalledWith('/test/path'); }); it('第二个测试:验证失败场景', async () => { // 给当前测试设置全新的Mock行为 mkdirpPromiseMock.mockRejectedValue(new Error('创建目录失败')); await expect(yourTargetModule.yourFunction('/invalid/path')) .rejects.toThrow('创建目录失败'); expect(mkdirpPromiseMock).toHaveBeenCalledWith('/invalid/path'); }); });
写法2:在每个测试用例内部单独加载模块
如果你的测试用例逻辑差异很大,也可以直接在每个it里重新创建Mock和加载模块,更直观:
const proxyquire = require('proxyquire'); describe('你的目标模块测试', () => { it('第一个测试:验证成功场景', async () => { const mkdirpPromiseMock = jest.fn().mockResolvedValue(true); const yourTargetModule = proxyquire('../path/to/your-target-module', { 'mkdirp-promise': mkdirpPromiseMock }); await yourTargetModule.yourFunction('/test/path'); expect(mkdirpPromiseMock).toHaveBeenCalled(); }); it('第二个测试:验证失败场景', async () => { const mkdirpPromiseMock = jest.fn().mockRejectedValue(new Error('权限不足')); const yourTargetModule = proxyquire('../path/to/your-target-module', { 'mkdirp-promise': mkdirpPromiseMock }); await expect(yourTargetModule.yourFunction('/root/path')) .rejects.toThrow('权限不足'); }); });
关键注意点
- 不要在
describe外层定义复用的Mock实例和proxyquire加载的模块,必须每次测试都重新创建/加载。 - 如果用Sinon代替Jest,逻辑完全一致:每次测试前创建新的
sinon.stub(),重新加载模块即可。 - 清除
require.cache的步骤不是必须的,但在复杂模块依赖场景下能彻底避免缓存遗留问题。
内容的提问来源于stack exchange,提问作者iAviator
相关产品推荐
相关产品推荐

