如何用Sinon Stub ES6类中的模块函数?测试遇报错且覆盖率不足
解决Sinon Stub
soapModule.callSoap 报错及测试覆盖问题 嘿,我来帮你搞定这个问题!你现在遇到两个麻烦:一是Stub callSoap 时弹出 Cannot stub non-existent own property callSoap 错误,二是sm.callSoap(id) 后面的then/catch代码没被测试覆盖。咱们一步步拆解原因,再给出可行的解决方案。
问题根源分析
先看你的soapModule定义:callSoap是构造函数里的箭头函数属性。这里有两个坑:
- 箭头函数不会被挂载到实例的自身可枚举属性上,Sinon没法识别它,所以报错说“找不到要Stub的属性”;
- 你在测试里Stub了一个单独的
soap实例,但MyClass.myMethod里每次都会新建new soapModule(),这个全新的实例根本没被Stub影响到,自然覆盖不到后续代码。
另外,测试异步代码时没等待Promise完成,测试会提前结束,这也是代码覆盖不全的原因之一。
解决方案1:修改soapModule为原型方法(推荐)
箭头函数不适合做需要被Stub的方法,改成原型方法后,Sinon可以轻松Stub整个类的所有实例:
修改module.js
function soapModule() { } // 把callSoap改成原型方法 soapModule.prototype.callSoap = function(id) { // ...你的原有代码 return new Promise((resolve, reject) => { resolve("whatever"); }); };
测试代码test.js(带异步处理和覆盖验证)
import MyClass from "../src/my-class"; import { soapModule } from "soap-client"; import sinon from 'sinon'; describe("Test MyClass", () => { let myClassInstance; let callSoapStub; before(() => { myClassInstance = new MyClass(); // Stub原型上的callSoap方法,影响所有新实例 callSoapStub = sinon.stub(soapModule.prototype, 'callSoap'); }); after(() => { // 测试后恢复原型方法,避免污染其他测试用例 callSoapStub.restore(); }); it("should handle successful callSoap response", async () => { const fakeResponse = { resp: "tada!" }; callSoapStub.resolves(fakeResponse); // 用await等待异步操作完成,确保后续代码执行 await myClassInstance.myMethod(1); // 验证callSoap被正确调用 sinon.assert.calledWith(callSoapStub, 1); // 验证then里的console.log执行了 const consoleLogStub = sinon.stub(console, 'log'); await myClassInstance.myMethod(1); sinon.assert.calledWith(consoleLogStub, fakeResponse); consoleLogStub.restore(); }); it("should handle callSoap error", async () => { const errorMessage = "soap request failed"; callSoapStub.rejects(new Error(errorMessage)); const consoleLogStub = sinon.stub(console, 'log'); await myClassInstance.myMethod(1); sinon.assert.calledWith(consoleLogStub, "Error :" + errorMessage); consoleLogStub.restore(); }); });
解决方案2:不修改soapModule,Stub构造函数返回假实例
如果没法改动soapModule的源码,我们可以直接Stub构造函数,让它返回一个带有Stubbed callSoap的假对象:
测试代码test.js
import MyClass from "../src/my-class"; import { soapModule } from "soap-client"; import sinon from 'sinon'; describe("Test MyClass", () => { let myClassInstance; let callSoapStub; let soapModuleConstructorStub; before(() => { myClassInstance = new MyClass(); // 创建callSoap的Stub callSoapStub = sinon.stub().resolves({ resp: "tada!" }); // Stub构造函数,返回带有Stub方法的假实例 soapModuleConstructorStub = sinon.stub(soapModule).returns({ callSoap: callSoapStub }); }); after(() => { soapModuleConstructorStub.restore(); }); it("should process callSoap result", async () => { await myClassInstance.myMethod(1); // 验证构造函数被调用 sinon.assert.calledOnce(soapModuleConstructorStub); // 验证callSoap被传入正确参数 sinon.assert.calledWith(callSoapStub, 1); // 验证then里的日志输出 const logStub = sinon.stub(console, 'log'); await myClassInstance.myMethod(1); sinon.assert.calledWith(logStub, { resp: "tada!" }); logStub.restore(); }); });
关键注意点
- 异步测试必须用
async/await或者返回Promise,否则测试会在异步代码执行前结束,导致代码覆盖不全; - 箭头函数作为实例属性时,Sinon无法直接Stub,因为它不是对象的可枚举自身属性;
- Stub单个实例没用,因为
myMethod每次都会新建实例,必须Stub原型或者构造函数。
内容的提问来源于stack exchange,提问作者Felipe Nuñez
相关产品推荐
相关产品推荐

