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

如何用Sinon Stub ES6类中的模块函数?测试遇报错且覆盖率不足

解决Sinon Stub soapModule.callSoap 报错及测试覆盖问题

嘿,我来帮你搞定这个问题!你现在遇到两个麻烦:一是Stub callSoap 时弹出 Cannot stub non-existent own property callSoap 错误,二是sm.callSoap(id) 后面的then/catch代码没被测试覆盖。咱们一步步拆解原因,再给出可行的解决方案。

问题根源分析

先看你的soapModule定义:callSoap是构造函数里的箭头函数属性。这里有两个坑:

  1. 箭头函数不会被挂载到实例的自身可枚举属性上,Sinon没法识别它,所以报错说“找不到要Stub的属性”;
  2. 你在测试里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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:28:06