Why use spyon with mock implemenation rather than jest.fn?

Viewed 1843

I have a hard time understanding when to use jest.fn, jest.mock or jest.spyOn.

This seems to be the summary:

  • Use fn when mocking a function
  • Use mock when mocking a module
  • Use spyOn when you want to observe things about a real module or function

But I see code examples like this:

test('creates Contract on correct date', () => {
  const NOW = '2019-05-03T08:00:00.000Z';
  const mockDateNow = jest
    .spyOn(global.Date, 'now')
    .mockImplementation(() => new Date(NOW).getTime());

  ...stuff...

  expect(result).toEqual(NOW);

  mockDateNow.mockRestore();
});

Is there any advantage to that over the simpler code below (excuse me if I have made any syntax errors).

test('creates Contract on correct date', () => {
  const NOW = '2019-05-03T08:00:00.000Z';
  const mockDateNow = global.Date.now.mockImplementation(() => new Date(NOW).getTime());

  ...stuff...

  expect(result).toEqual(NOW);

  mockDateNow.mockRestore();
});

So,

why is spyOn used to mock a single function that then get's a mock implementation? Is my understanding of when to use fn, mock, spyOn wrong? Please show me how I am wrong.

1 Answers

jest.fn creates Jest spy (technically a stub) with no implementation, it doesn't matter how it's used then. jest.spyOn replaces a method or property accessor with Jest spy (technically a spy), which implementation defaults to original one and can be changed to anything, including a stub.

Assigning a spy explicitly as a method of an object that exists outside current test is a bad practice because this prevents Jest from restoring original implementation in case it's needed later and may result in test cross-contamination. This should be never done:

Date.now = jest.fn().mockImplementation(...);

Instead, it should be:

jest.spyOn(Date, 'now').mockImplementation(...);

It's a good practice to always use restoreMocks configuration option or jest.restoreAllMocks() in beforeEach to prevent tests affecting each other through common mock implementations.

As long as Date.now is Jest spy, the implementation can be changed with Date.now.mockImplementation(...), there's no need to use jest.spyOn on it multiple times.

Related