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

使用Sinon存根ES模块命名导出函数时报错:TypeError相关问题

Sinon存根ES模块命名导出报错的解决方案

问题现象

项目已在package.json中设置"type": "module",使用Sinon测试时出现以下差异:

  • 对默认导出的对象内函数(如foos2.foo2)存根可正常工作
  • 对命名导出的函数(如foos1.foo1)执行Sinon.stub(foos1, "foo1")时,抛出错误:TypeError: ES Modules cannot be stubbed

根本原因

ES模块的命名导出属于只读绑定,Sinon无法直接修改这些绑定来完成存根;而默认导出如果是普通对象,对象的属性是可写的,因此能被Sinon修改存根。

可行解决方案

方案1:将命名导出函数包装为对象属性

修改foo1.ts,把命名导出的函数放到对象中导出:

// foo1.ts
export const foos1 = {
  foo1: () => {
    // 原foo1函数逻辑
  }
};

在业务代码和测试文件中通过对象访问该函数:

// bar.ts
import { foos1 } from './foo1.ts';
foos1.foo1();
// bar.test.ts
import { foos1 } from './foo1.ts';
import sinon from 'sinon';

const foo1Stub = sinon.stub(foos1, "foo1").returns('stubbed value');

方案2:使用Sinon的模块拦截功能(Sinon v15+支持)

利用Sinon的sinon.stubModule直接拦截模块导出:

// bar.test.ts
import sinon from 'sinon';

// 拦截foo1模块的命名导出foo1
const foo1Stub = sinon.stubModule('./foo1.ts', {
  foo1: sinon.stub().returns('stubbed value')
});

注意:需确保测试运行器支持ES模块加载,且Sinon版本不低于v15。

方案3:通过依赖注入重构代码

修改bar.ts,让函数接收依赖参数,而非直接导入模块:

// bar.ts
export function bar(foo1Func: () => void, foo2Func: () => void) {
  foo1Func();
  foo2Func();
}

测试时直接传入存根函数:

// bar.test.ts
import sinon from 'sinon';
import { bar } from './bar.ts';

const foo1Stub = sinon.stub().returns('stubbed value');
const foo2Stub = sinon.stub().returns('stubbed value');

bar(foo1Stub, foo2Stub);
// 断言存根被调用
sinon.assert.calledOnce(foo1Stub);

方案4:使用ES模块兼容的Mock工具

比如rewiremock,示例如下:

// bar.test.ts
import rewiremock from 'rewiremock';
import sinon from 'sinon';

rewiremock('./foo1.ts')
  .with({ foo1: sinon.stub().returns('stubbed value') })
  .start();

// 导入bar模块,此时它会使用mock后的foo1
import { bar } from './bar.ts';

// 执行测试逻辑
bar();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:05:22