Search code examples
typescriptunit-testingjestjsmockingts-jest

How to mock module with both Constructor and function with Jest?


I am new with Jest, got stuck and none of the answers I found online work. I need to mock a module, which contains both Class ("Client") and one function ("getCreds"). Class Client then contains function Login. This is how it looks in the code I would like to test

import * as sm from 'some-client';
const smCli: sm.Client = new sm.Client();

export const getKey = async (): void => {
    const smCreds = await sm.getCreds();
    await smCli.login(smCreds);
};

Problem is that while I can easily mock getCreds function, I have no idea how to mock login function of the instance of Client and test getKey function properly. I tried various lines similar to this one, but none works. Could someone advise where I am making a mistake? Thank you.

import * as sm from 'some-client';
jest.mock('some-client');

const smClientMock = sm.Client as jest.Mock<unknown>
const smGetCredsMock = sm.getCreds as jest.Mock<Promise<unknown>>

smGetCredsMock.mockResolvedValue(1);
smClientMock.mockImplementation(() => {
    return {
        login: () => {
            return 2;
        }
    };
});

Solution

  • You can use mockFn.mock.instances to get the mocked instance of sm.Client class. So that you can assert .login() method on the instance.

    E.g.

    some-client.ts:

    export class Client {
      async login(creds) {}
    }
    
    export const getCreds = async () => ({ pwd: 'real pwd' });
    

    index.ts:

    import * as sm from './some-client';
    const smCli: sm.Client = new sm.Client();
    
    export const getKey = async () => {
      const smCreds = await sm.getCreds();
      await smCli.login(smCreds);
    };
    

    index.test.ts:

    import * as sm from './some-client';
    import { getKey } from './';
    
    jest.mock('./some-client');
    
    const smClientMock = sm.Client as jest.MockedClass<typeof sm.Client>;
    const smGetCredsMock = sm.getCreds as jest.MockedFunction<typeof sm.getCreds>;
    
    describe('74516778', () => {
      test('should pass', async () => {
        smGetCredsMock.mockResolvedValue({ pwd: '123' });
        await getKey();
        expect(smClientMock).toBeCalledTimes(1);
        expect(smGetCredsMock).toBeCalledTimes(1);
        const smClientInstanceMock = smClientMock.mock.instances[0];
        expect(smClientInstanceMock.login).toBeCalledWith({ pwd: '123' });
      });
    });
    

    Test result:

     PASS  stackoverflow/74516778/index.test.ts (8.48 s)
      74516778
        ✓ should pass (3 ms)
    
    ----------------|---------|----------|---------|---------|-------------------
    File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------------|---------|----------|---------|---------|-------------------
    All files       |   76.92 |      100 |   33.33 |    87.5 |                   
     index.ts       |     100 |      100 |     100 |     100 |                   
     some-client.ts |      50 |      100 |       0 |   66.67 | 2                 
    ----------------|---------|----------|---------|---------|-------------------
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        9.278 s, estimated 10 s
    

    package version:

    "jest": "^26.6.3",