Search code examples
node.jstypescriptsinon

How to use sinon (or something else) to check that es6 ts class was created with parameters


Let's say I am using auth0 in typescript in Node.js

I have this class I want to test:

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;

Now I want to write a unit test that confirms that the right values were passed into options.

I am trying sth like

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');
    });
})

This gives me:

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


Solution

  • We can't stub the constructor of a JS class like that, see issues/1892.

    A solution is to use proxyquire to stub out the auth0 module and its AuthenticationClient class. Besides, we can use .getCall(n).args to get the arguments of stub for the nth call.

    E.g.

    classToTest.ts:

    import { AuthenticationClient } from './auth0';
    
    export class ClassToTest {
      public static getClient(): AuthenticationClient {
        let options = {
          domain: global.myConfig.DOMAIN,
          clientId: global.myConfig.CLIENT,
          clientSecret: global.myConfig.SECRET,
        };
        let auth0 = new AuthenticationClient(options);
    
        return auth0;
      }
    }
    
    export default ClassToTest;
    

    auth0.ts:

    // Simulate auth0 module
    export class AuthenticationClient {
      constructor(options) {}
    }
    

    classToTest.test.ts:

    import proxyquire from 'proxyquire';
    import sinon from 'sinon';
    
    describe('76519308', () => {
      beforeEach(() => {
        global.myConfig = global.myConfig || {};
        global.myConfig.DOMAIN = 'someValue';
      });
    
      it('should pass', () => {
        const AuthenticationClientStub = sinon.stub();
        const ClassToTest = proxyquire('./classToTest', {
          './auth0': {
            AuthenticationClient: AuthenticationClientStub,
          },
        }).default;
        ClassToTest.getClient();
        sinon.assert.match(AuthenticationClientStub.getCall(0).args[0].domain, 'someValue');
      });
    });