You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:30:11