How to reset a specific method of a module with resetAllMocks in Jest?

Viewed 382

I've been researching for quite a while for this, none of the answers are solving the problem. I have resetAllMocks afterEach tests set. I want to mock a specific method of a module.

If I:

import * as ReactRouter from 'react-router';
const mockPush = jest.fn();
jest.mock('react-router', () => ({
  ...jest.requireActual('react-router'),
  useHistory: () => {
    return { push: mockPush };
  },
  useRouteMatch: jest.fn().mockReturnValue({ url: '' })
}));

It works well at the first test case, but at the second it throws: TypeError: Cannot destructure property 'url' of 'react_router_1.useRouteMatch(...)' as it is undefined.

If I:

import * as ReactRouter from 'react-router';
const mockPush = jest.fn();

beforeEach(() => {
  jest.mock('react-router', () => ({
    ...jest.requireActual('react-router'),
    useHistory: () => {
      return { push: mockPush };
    },
    useRouteMatch: jest.fn().mockReturnValue({ url: '' })
  }));
})

It doesn't do anything (not mocking the values).

If I:

import * as ReactRouter from 'react-router';
jest.mock('react-router');
const mockPush = jest.fn();

beforeEach(() => {
  jest.spyOn(ReactRouter, 'useHistory').mockReturnValue({ push: mockPush } as any)
  jest.spyOn(ReactRouter, 'useRouteMatch').mockReturnValue({ url: '' } as any)
})

It just doesn't do anything (breaking other functionalities without throwing any error).

If I:

import * as ReactRouter from 'react-router';
const mockPush = jest.fn();

beforeEach(() => {
  jest.spyOn(ReactRouter, 'useHistory').mockReturnValue({ push: mockPush } as any)
  jest.spyOn(ReactRouter, 'useRouteMatch').mockReturnValue({ url: '' } as any)
})

It throws: TypeError: Cannot redefine property: useHistory at Function.defineProperty (<anonymous>).

Not sure if it's such an uncommon thing to reset all mocks after tests so no need to remock, but I couldn't find a suitable solution for remocking before each test.

1 Answers

Finally I managed to solve the issue by first mocking the target methods first only with jest.fn(), and importing the rest with jest.requireActual.

Then inside beforeEach doing the mock implementation like:

import * as ReactRouter from 'react-router';

jest.mock('react-router', () => ({
  ...jest.requireActual('react-router'),
  useHistory: jest.fn(),
  useRouteMatch: jest.fn()
}));

const mockPush = jest.fn();

beforeEach(() => {
  jest.spyOn(ReactRouter, 'useHistory').mockReturnValue({ push: mockPush } as any);
  jest.spyOn(ReactRouter, 'useRouteMatch').mockReturnValue({ url: '' } as any);
});

Related