为何调用sinon.createStubInstance仍会触发类构造函数?
为什么用
sinon.createStubInstance还是触发了类的构造函数? 哈哈,这个问题我之前踩过坑!明明照着官方文档用了sinon.createStubInstance,结果构造函数还是跑起来了,着实头疼。下面给你拆解几个最常见的原因和解决办法:
1. Sinon版本太老,对ES6类支持不完善
早期版本的Sinon(比如v10及以前)在处理ES6类的createStubInstance时,可能存在兼容性问题,导致构造函数被意外调用。毕竟官方文档里的行为是针对较新版本的Sinon来说的。
解决办法:
- 先检查你的Sinon版本:运行
npm list sinon或者yarn list sinon查看版本号 - 升级到最新稳定版:执行
npm update sinon(npm用户)或者yarn upgrade sinon(yarn用户),升级后再测试应该能解决大部分问题。
2. 类的导出/导入方式错了
这是最容易忽略的点!如果你的类不是直接导出的,而是被包裹在对象里,或者导入时没正确拿到构造函数本身,createStubInstance就会失效,甚至触发构造函数。
举个例子:
错误的导出方式
// Fizz.js 错误写法 module.exports = { Fizz: class Fizz { constructor() { console.log('构造函数执行了!') } } }
这种情况下,你导入的Fizz其实是一个对象,而不是构造函数本身,调用sinon.createStubInstance(Fizz)自然不对,甚至会触发构造函数。
正确的导出/导入方式
- 如果是CommonJS导出:
// Fizz.js 正确写法 module.exports = class Fizz { constructor() { console.log('构造函数执行了!') } } // 测试文件导入 const Fizz = require('../Fizz')
- 如果是ES6默认导出:
// Fizz.js 正确写法 export default class Fizz { constructor() { console.log('构造函数执行了!') } } // 测试文件用CommonJS导入时要加.default const Fizz = require('../Fizz').default
3. 测试代码里不小心手动实例化了类
仔细检查你的测试代码,看看有没有在beforeEach、afterEach或者测试用例的其他地方,不小心写了new Fizz()或者间接触发构造函数的逻辑。这种情况很容易被忽略,导致你误以为是createStubInstance的问题。
验证方法:
可以在类的构造函数里加个console.log,运行测试时看输出时机,就能定位到哪里触发了构造函数。
终极兜底方案:手动存根构造函数
如果以上方法都不管用(比如你的类被装饰器修改过,或者有特殊的静态逻辑),可以直接用sinon.stub()手动拦截构造函数:
const sinon = require('sinon') const Fizz = require('../Fizz') describe('Fizz', () => { it('should mock without triggering constructor', () => { // 存根构造函数,让它返回自定义的存根对象 const fizzStub = sinon.stub(Fizz).returns({ // 在这里定义你需要的存根方法/属性 someMethod: sinon.stub().returns('mock result') }) // 此时调用new Fizz()不会执行原构造函数,只会返回你定义的对象 const instance = new Fizz() sinon.assert.calledOnce(Fizz) // 可以断言构造函数被调用了,但原逻辑没执行 }) })
内容的提问来源于stack exchange,提问作者AfterWorkGuinness
相关产品推荐
相关产品推荐

