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

如何用testdouble.js间接模拟ESM依赖?附替代工具咨询

问题解答

1. 用testdouble.js正确模拟ESM间接依赖的方法

ESM采用静态模块缓存机制,模块一旦被加载,后续导入都会复用缓存版本。要让utils.mjs使用模拟后的subdep.mjs,核心是确保utils.mjs在subdep.mjs完成模拟后才被加载,具体操作如下:

步骤1:清除模块缓存(关键)

每次测试前清除Node.js模块缓存中subdep.mjs和utils.mjs的缓存,避免之前的加载结果干扰当前测试:

import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

function clearModuleCache(modulePath) {
  const resolvedPath = join(__dirname, '../src', modulePath);
  const moduleUrl = new URL(`file://${resolvedPath}`).href;
  
  // 清除CommonJS缓存
  delete require.cache[moduleUrl];
  // 清除ESM加载缓存
  if (globalThis.module?.children) {
    globalThis.module.children = globalThis.module.children.filter(child => 
      child.filename !== resolvedPath
    );
  }
}

步骤2:先模拟模块,再导入依赖模块

在测试用例中,先调用td.replace完成subdep.mjs的模拟,再导入utils.mjs,确保utils加载时使用的是模拟后的依赖:

import td from 'testdouble';
import { expect } from 'chai';

describe('utils.mjs', () => {
  beforeEach(() => {
    // 清除目标模块缓存,重置testdouble状态
    clearModuleCache('subdep.mjs');
    clearModuleCache('utils.mjs');
    td.reset();
  });

  it('fetchData 应调用模拟的 myFunction', async () => {
    // 1. 先模拟subdep.mjs的myFunction
    const mockedSubdep = td.replace('../src/subdep.mjs');
    td.when(mockedSubdep.myFunction()).thenReturn('模拟返回值');

    // 2. 再导入utils.mjs(此时会使用已模拟的subdep)
    const { fetchData } = await import('../src/utils.mjs');

    // 3. 执行测试并验证
    const result = await fetchData();
    expect(result).to.equal('模拟返回值');
    td.verify(mockedSubdep.myFunction());
  });
});

注意事项

  • 确保项目package.json中设置"type": "module",启用ESM支持;
  • 若使用Mocha等测试框架,部分Node.js版本可能需添加--experimental-vm-modules参数运行测试。

2. 支持ESM间接依赖模拟的替代工具

如果觉得testdouble.js处理ESM间接mock流程繁琐,可以考虑以下工具:

  • Vitest:天生支持ESM,模块mock机制简洁,默认支持拦截间接依赖调用。只需通过vi.mock('../src/subdep.mjs', () => ({ myFunction: vi.fn().mockReturnValue('模拟值') }))即可完成模拟,无需额外处理缓存;
  • Jest:对ESM支持已成熟,通过jest.mock可轻松实现间接依赖mock,只需在配置文件中启用ESM支持即可;
  • Sinon.js + proxyquire-esm:Sinon负责创建模拟函数,proxyquire-esm用于替换ESM模块的依赖,可实现间接mock,但配置相对繁琐。

内容的提问来源于stack exchange,提问作者Naremy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 05:12:44