Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jest - mock a named class-export in typescript

I have a node module which exports a few classes, one of which is Client, which I use to create a client (having a few APIs as methods).

I'm trying to test my module which uses this node module as a dependency using Jest. However, I've been unable to successfully mock the one method (say search()) in the Client class.

Here is my spec for myModule:

//index.spec.ts
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
  beforeAll(() => {
    nock.disableNetConnect();
  });
  it('test search method in my module', () => {
    jest.mock('node-module-name');

    const mockedClient = <jest.Mock<externalModule.Client>>externalModule.Client;

    const myClient = createClient({/*params*/}); //returns instance of Client class present in node module by executing Client() constructor
    myClient.searchByName('abc'); //calls search API - I need to track calls to this API

    expect(mockedClient).toHaveBeenCalled();
    expect(mockedClient.prototype.search).toHaveBeenCalledWith('abc');
  });
});

This, however, doesn't create a mock at all and triggers a nock error since the search API tries to connect to the url (given through params).

I've also tried mocking the Client class like the following. While successfully creating a mock for the Client class and also the search API (verified that search() is also mocked through console logs), it gives me an error while I try to check if search() has been called.

externalModule.Client = jest.fn(() => { return { search: jest.fn(() => Promise.resolve('some response')) } });
//creates the mock successfully, but not sure how to track calls to 'search' property

const client = myModule.createClient(/*params*/);
client.searchByName('abc');

expect(externalModule.Client).toHaveBeenCalled(); //Successful
expect(externalModule.Client.prototype.search).toHaveBeenCalled(); //returns error saying "jest.fn() value must be a mock function or spy, Received: undefined"

I'm not sure what I'm doing wrong. Thank you in advance.

like image 432
deadlock Avatar asked Mar 05 '23 09:03

deadlock


2 Answers

Mocking whole module

Try moving jest.mock to the top of file

//index.spec.ts
const search = jest.fn();
jest.mock('node-module-name', () => ({
  Client: jest.fn(() => ({ search }))
}));
import * as nock from 'nock';
import * as externalModule from 'node-module-name';
import { createClient } from './../../src/myModule';
describe(() => {
  beforeAll(() => {
    nock.disableNetConnect();
  });
  it('test search method in my module', () => {
    const myClient = createClient({/*params*/});
    myClient.searchByName('abc'); 

    expect(externalModule.Client).toHaveBeenCalled();
    expect(search).toHaveBeenCalledWith('abc');
    externalModule.Client.mockClear();
    search.mockClear();
  });
});

Mocking only Client

Create search constant and track it.

const search = jest.fn();
externalModule.Client = jest.fn(() => ({ search }));

const client = myModule.createClient(/*params*/);
client.searchByName('abc');

expect(externalModule.Client).toHaveBeenCalled();
expect(search).toHaveBeenCalled();
like image 137
paibamboo Avatar answered Mar 12 '23 16:03

paibamboo


Here is how I mocked it. I had to change naming and removing some code to avoid exposing original source.

jest.mock('../foo-client', () => {
  return { FooClient: () => ({ post: mockPost }) }
})

Full code.

// foo-client.ts

export class FooClient {
    constructor(private config: any)
    post() {}
}

// foo-service.ts

import { FooClient } from './foo-client'

export class FooLabelService {
    private client: FooClient
    constructor() {
        this.client = new FooClient()
    }

    createPost() {
        return this.client.post()
    }
}

// foo-service-test.ts

import { FooService } from '../foo-service'

const mockPost = jest.fn()
jest.mock('../foo-client', () => {
  return { FooClient: () => ({ post: mockPost }) }
})

describe('FooService', () => {
  let fooService: FooService

  beforeEach(() => {
    jest.resetAllMocks()
    fooService = new FooService()
  })

  it('something should happened', () => {
    mockPost.mockResolvedValue()
    fooService.createPost()
  })
})
like image 29
Rashmin Javiya Avatar answered Mar 12 '23 17:03

Rashmin Javiya