简体   繁体   中英

Check if functions have been called in unit test

Hi I'm trying to write some unit tests in Jest for a module I write, but kind of stuck currently and need some advice how to continue.

export const submitOrder = async (body, key) => {
  const clientRepo = new ClientRepository(db)
  const companyRepo = new CompanyRepository(db)

  const company = await getCompanyByKey(
    companyRepo,
    key
  );

  const client = await createClient(
    clientRepo,
    body
  );

  await addClientToCompany(
    companyRepo,
    client.id,
    company.id
  );

  .. More things
}

I can easily test each function( getCompanyByKey , createClient & addClientToCompany ) by passing down a mocked repository.

But I would also like to test my "flow" of the submitOrder function, by checking if my repository functions have been called. But I would then need the instance of each repository, which I don't instantiate until my submitOrder function.

Something like this, which is similar how I unit test my functions.

jest.mock('../repositories/ClientRepository');
jest.mock('../repositories/CompanyRepository');

test('should be able to submit an order', async () => {
  const apiKey = 'mocked-super-key';
  const body = getMockData();

  const result = await submitOrder(body, apiKey);
  expect(result).toMatchSnapshot();
  expect(CompanyRepository.findByKey).toHaveBeenCalled();
  expect(ClientRepository.create).toHaveBeenCalled();
  expect(CompanyRepository.addClient).toHaveBeenCalled();
});

Do you have any tips of how I can test if my repositories have been called?

The problem you describe is one of the motivating factors behind dependency injection.

As a single example: your submitOrder() code uses new to directly instantiate a client repository of the specific implementation ClientRepository . Instead, it could declare that it has a dependency - it needs an object that implements the interface of a client repository. It could then allow for such an object to be supplied by the surrounding environment (a "dependency injection container" in buzzword-ese). Then during testing you would create and provide ("inject") a mock implementation instead of the real implementation.

This has the added benefit that if you ever have to be able to select between multiple "real" implementations, you're already set up to do that too.

There are many ways to achieve this. It can be as simple as a design pattern, or for a more complete solution you could use a dependency injection framework.

If you absolutely cannot refactor your code for this practice, then JavaScript is dynamic enough that you can probably cobble together a way to intercept the invocation of new and thereby simulate dependency injection.

You can pass a mock implementation factory as a second parameter to jest.mock , as described in the docs .

You can use this to mock out the methods that you want to check to have been called.

Try this:

jest.mock('../repositories/CompanyRepository', () => {
    findByKey: jest.fn(),
    addClient: jest.jn()
});

const mockCreate = jest.fn();

jest.mock('../repositories/CompanyRepository', () => class {
    create(...args) {
        mockCreate(...args);
    }
});

test('should be able to submit an order', async () => {
    const apiKey = 'mocked-super-key';
    const body = getMockData();

    const result = await submitOrder(body, apiKey);
    expect(result).toMatchSnapshot();
    expect(CompanyRepository.findByKey).toHaveBeenCalled();
    expect(ClientRepository.create).toHaveBeenCalled();
    expect(CompanyRepository.addClient).toHaveBeenCalled();
});

Since CompanyRepository is created with “new”, we use a class definition in this case and pass in a mock function that is called when the “create” method is invoked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM