jest: can't unmock module per test

I want to mock a module and then unmock it for specific tests. As an example my main file looks like:

import React from 'react';
import LocalizedStrings from 'react-localization';

const strings = new LocalizedStrings({
 en:{
    optSelect:'Select',
    lblDisplayName:"Display Name",

 },
 fr: {
    optSelect:"Select_fr",
    lblDisplayName:"Display Name_fr",
 }
});

export default class MyComponent extends React.Component{
  
  constructor(props) {
    super(props);
  }

  render(){//some html
  }
} 

In my test i want to do something like below:

jest.mock('react-localization',()=>{
  return jest.fn();
});

import MyComponent from './MyComponent';

import localizedStrings from 'react-localization';
it('initializes localization with correct values',()=>{
    expect(localizedStrings.mock.calls[0][0]).toMatchSnapshot();
});

//unmock react-localization for <MyComponent /> from here on for future tests
//i tried using jest.unmock('react-localization') but then it doesnot mock it all and 1st test fails.

it('matches snapshot',()=>{
  const wrapper= shallow(<MyComponent />);
  expect(toJson(wrapper)).toMatchSnapshot();
});

Any suggestion or help is appreciated.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 42
  • Comments: 35 (4 by maintainers)

Most upvoted comments

How about when you have manual mocks? How do you unmock them per test?

I solved the problem with the following approach:

import moduleToMock from 'moduleToMock'

jest.mock('moduleToMock')

describe('top level', () => {
  it('mock', () => {
    moduleToMock.log.mockImplementationOnce(() => true)

    // ...
  })

  it('do not mock', () => {
    moduleToMock.log.mockImplementationOnce(
      require.requireActual('moduleToMock').default.log
    )

    // ...
  })
})

Does someone have a full example for mock/unmocking modules for test?

You can do require.requireActual('my-mock') in Jest to get the real module.

Why was this ticket closed? Was a solution found for this? I haven’t been able to pick when to use the non mocked module on a per test basis.

@cpojer If there is no solution to this problem, then you should reopen this issue or otherwise provide answer please.

@cpojer @ebrentnelson @simonsmith Just on a note that found a workaround as follows:

jest.mock('react-localization',()=>{
  return jest.fn(()=>{
    const actual = require.requireActual('react-localization');
    return new actual(val) ;
 });
});

but would be nice if we can mock and unmock submodules per test.

+1

It’ll only work if you call jest.resetModules() before the calls to doMock.

None of the suggested approaches work for me when trying to mock an implicit import (react-redux in this case). Searching for this topic on google yields this issue and not any jest docs with a canonical example. It would be nice if we could get either a definitive, working example from the jest team, or confirmation that this isn’t possible.

As an example for those trying to do something similar, for this test I wanted to mock react-redux for a snapshot test, but not for the other test. This worked for me:

import React from 'react'
import ReactDOM from 'react-dom'
import { shallow } from 'enzyme'
import App from './App'

beforeEach(() => {
    jest.resetModules();
});

describe('<App/>', () => {
    it('renders without crashing', () => {
        const div = document.createElement('div')

        ReactDOM.render(<App />, div)
        ReactDOM.unmountComponentAtNode(div)
    })

    it('renders the expected tree', () => {
        jest.doMock('react-redux', () => ({
            connect: jest.fn(() => component => `Connected${component.name}`),
            Provider: () => <div />,
        }))
        const App = require('./App').default
        const tree = shallow(<App />)

        expect(tree).toMatchSnapshot()
    })
})

You can use “spyOn” function inside any test to mock function and then use

  beforeEach(() => {
    jest.restoreAllMocks()
  })

//it will restore all functions mocked

you can use "spyOn" as following:
`    jest.spyOn(bcrypt, 'compare').mockImplementation((arg1, arg2) => true)`

Another possible solution is:

import { persistStore, persistReducer } from 'redux-persist';

jest.mock('redux-persist', () => ({
  persistStore: jest.fn(jest.requireActual('redux-persist').persistStore),
  persistReducer: jest.fn(jest.requireActual('redux-persist').persistReducer)
}));

...

expect(persistStore).toBeCalled();

...

+1

For example how to make this work if router is implicitly imported

describe('', () => {
    it('should render itself', () => {
        jest.dontMock('react-router-dom');
        const app = mount(<MainApp />);
        expect(app.exists()).toBe(true);
    });
    it('should redirect to 404 if invalid path', () => {
        // here nothing need to mock in respect of default automocking behaviour
        const wrapper = mount(
            <MemoryRouter initialEntries={['/random']}>
                <MainApp />
            </MemoryRouter>
        );
        expect(wrapper.find(NotFound)).toHaveLength(1);
});

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

My current n wretched ‘solution’ is to separate tests into several files. Not sure if that’s what they call “simple” and “delightful”.

I tried using jest.doMock instead of jest.mock:

afterEach(() => {
  jest.dontMock("a");
});

test("a1", () => {
  jest.doMock("a", () => jest.fn(() => 1));
  const a = require("a").default;
  expect(a()).toBe(1);
});

test("a2", () => {
  jest.doMock("a", () => jest.fn(() => 2));
  const a = require("a");
  expect(a()).toBe(2);
});

but in both tests a() yields 1, just like with jest.mock. I’m not sure what jest.doMock does exactly. 😕