使用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
相关产品推荐
相关产品推荐

