
Sinon 的 createStubInstance 不会执行构造函数,导致类中通过构造函数初始化的属性(如 public readonly prop)在存根实例上并不存在,因此直接 sinon.stub(obj, 'prop').value(...) 会报 “Cannot stub non-existent property” 错误;需先确保属性存在(如通过类型断言赋值),再进行存根。
sinon 的 `createstubinstance` 不会执行构造函数,导致类中通过构造函数初始化的属性(如 `public readonly prop`)在存根实例上并不存在,因此直接 `sinon.stub(obj, 'prop').value(...)` 会报 “cannot stub non-existent property” 错误;需先确保属性存在(如通过类型断言赋值),再进行存根。
在使用 Sinon 进行单元测试时,sinon.createStubInstance(Class) 是快速生成类存根实例的常用方式——它自动为类的所有方法创建存根(返回 undefined 或可配置行为),但不会调用构造函数。这意味着:若类的属性(如 public readonly myParameter: string)仅在构造函数中通过 this.myParameter = myParameter 初始化,则该属性在存根实例上根本不存在(in 操作符返回 false),sinon.stub(instance, 'myParameter') 自然会失败。
✅ 正确做法是:先让属性“存在”,再存根。由于 TypeScript 编译后属性实际挂载在实例上,我们可通过类型断言强制写入一个占位值(如 null 或 undefined),使属性被创建:
import MyClass from "../src/index";
import sinon, { SinonStubbedInstance } from "sinon";
describe("stubs", () => {
it("should stub a constructor-initialized property", () => {
const myClass = sinon.createStubInstance(MyClass) as SinonStubbedInstance<myclass>;
// ✅ 关键步骤:确保属性存在(绕过 readonly 限制)
(myClass as any).myParameter = undefined;
// ✅ 现在可以安全存根
sinon.stub(myClass, "myParameter").value("a");
expect(myClass.myParameter).toBe("a"); // ✅ 通过
});
});</myclass>
⚠️ 注意事项:
-
as any是必要的临时绕过,因readonly修饰符阻止编译时赋值,但运行时 JS 对象属性可动态添加/修改; - 若属性类型较复杂(如另一个类实例),同样适用该模式:
const complexStub = sinon.createStubInstance(MyComplexClass); (complexStub as any).dependency = null; // 先占位 sinon.stub(complexStub, "dependency").value(sinon.createStubInstance(ExternalService));
- 替代方案(更语义化):不使用
createStubInstance,改用sinon.stub()手动构造对象:const myClass = { myParameter: "a", someMethod: sinon.stub() } as unknown as MyClass;但会丢失
createStubInstance对类方法的自动存根能力。
总结:createStubInstance 的设计目标是模拟方法行为,而非完整复现实例状态。当测试逻辑强依赖特定属性值时,请主动初始化属性再存根——这是 Sinon 与 TypeScript 协作中的典型实践模式。










