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

如何用Sinon测试TS类实例化参数?解决TS2345类型错误

问题:TypeScript中用Sinon Stub Auth0构造器时出现类型错误

我在Node.js的TypeScript环境中使用Auth0,现有待测试类ClassToTest,其静态方法getClient会基于全局配置创建AuthenticationClient实例并返回。我希望编写单元测试验证传入AuthenticationClient的options参数值是否正确,尝试用Sinon stub构造器时出现TypeScript错误:

error TS2345: Argument of type 'constructor' is not assignable to parameter of type 'keyof AuthenticationClient'

相关代码

ClassToTest.ts

import { AuthenticationClient, AuthenticationClientOptions } from 'auth0';

export class ClassToTest {
    public static getClient(): AuthenticationClient {
        let options = {
            domain: global.myConfig.DOMAIN,
            clientId: global.myConfig.CLIENT,
            clientSecret: global.myConfig.SECRET
        } as AuthenticationClientOptions;
        let auth0 = new AuthenticationClient(options);

        return auth0;
    }
}

export default ClassToTest;

测试代码(原错误版本)

import { expect } from "chai";
import * as sinon from 'sinon';

import ClassToTest from "./ClassToTest";
import { AuthenticationClient, AuthenticationClientOptions } from "auth0";

describe("ClassToTest", () => {
    beforeEach(() => {
       global.myConfig.DOMAIN = 'someValue';
    });
    
    afterEach(() => {
        sinon.restore();
    });

    it("uses right options", async function () {
        let usedOptions: AuthenticationClientOptions;
        sinon.stub(AuthenticationClient.prototype, 'constructor')
            .callsFake((opts: AuthenticationClientOptions) => {
                usedOptions = opts;
            });
        expect(usedOptions.domain).to.equal('someValue');
    });
});

解决方法

Sinon无法直接stub类原型上的constructor属性(TypeScript类型定义中constructor不属于AuthenticationClient的实例键集合),正确做法是直接stub整个AuthenticationClient类的构造函数:

修改后的测试代码

import { expect } from "chai";
import * as sinon from 'sinon';

import ClassToTest from "./ClassToTest";
import { AuthenticationClient, AuthenticationClientOptions } from "auth0";

describe("ClassToTest", () => {
    beforeEach(() => {
       global.myConfig = {
           DOMAIN: 'someValue',
           CLIENT: 'test-client-id',
           SECRET: 'test-secret'
       };
    });
    
    afterEach(() => {
        sinon.restore();
    });

    it("uses right options", function () {
        let usedOptions: AuthenticationClientOptions;
        // 直接 stub 整个 AuthenticationClient 类的构造函数
        const authStub = sinon.stub(AuthenticationClient, 'constructor')
            .callsFake((opts: AuthenticationClientOptions) => {
                usedOptions = opts;
                // 返回一个空对象模拟实例,避免后续代码报错
                return {} as AuthenticationClient;
            });

        // 触发构造函数调用
        ClassToTest.getClient();

        // 断言参数是否正确
        expect(usedOptions?.domain).to.equal('someValue');
        expect(usedOptions?.clientId).to.equal('test-client-id');
        expect(usedOptions?.clientSecret).to.equal('test-secret');
        
        // 验证构造函数被调用一次
        expect(authStub.calledOnce).to.be.true;
    });
});

关键说明

  1. 直接Stub类本身:通过sinon.stub(AuthenticationClient, 'constructor')替换类的构造函数,而非原型上的constructor,这样能避开TypeScript的类型检查问题。
  2. 触发构造调用:必须调用ClassToTest.getClient(),否则构造函数不会执行,usedOptions会是undefined。
  3. 返回模拟实例:在fake函数中返回一个符合AuthenticationClient类型的空对象,避免原代码中返回实例时出现类型错误。

内容的提问来源于stack exchange,提问作者zaitsman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:17:07