Invalid hook call when using jest.resetModules
or jest.resetModuleRegistry
with dynamic require
in test
Steps to reproduce the behavior:
No invalid hook call
System:
OS: macOS Mojave 10.14.6
CPU: (8) x64 Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
Binaries:
Node: 10.16.3 - ~/.nvm/versions/node/v10.16.3/bin/node
Yarn: 1.17.3 - /usr/local/bin/yarn
npm: 6.11.3 - ~/.nvm/versions/node/v10.16.3/bin/npm
npmPackages:
jest: 24.9.0 => 24.9.0
I am also facing the same issues when I import react component(using hooks inside the component) using require and jest.resetModules() in the test.
@kapral18 We are also experiencing the same issue. Have you found a work around for this?
Same issue here
@lukevella Sorry for late response.
Here is what I came up with:
Seems like if you wrap each jest.doMock()
call into a jest.isolateModules()
call in each test, - it actually re-mocks the modules, as opposed to basic behavior when the mocks are unchanged.
And when you need to "reset" the mock, you doMock
it again but with jest.requireActual
call.
I have also been running into this issue in a particularly nasty way. We use the resetModules
config option globally, so tricks around calling jest.isolateModules()
don't work well. Instead, I came up with a workaround that globally excludes React from jest's resetModules
behavior.
In a file specified in the setupFiles
config option, add the following:
let mockActualReact;
jest.doMock(moduleName, () => {
if (!mockActualReact) {
mockActualReact = jest.requireActual('react');
}
return mockActualReact;
});
This sets up a mock for React that lazily loads the actual React, and always returns the result of the first initialization. This could also be generalized to implement a _set_ of modules that are globally excluded from resetModules
.
Generalized version:
const RESET_MODULE_EXCEPTIONS = [
'react',
'react-redux',
];
let mockActualRegistry = {};
RESET_MODULE_EXCEPTIONS.forEach(moduleName => {
jest.doMock(moduleName, () => {
if (!mockActualRegistry[moduleName]) {
mockActualRegistry[moduleName] = jest.requireActual(moduleName);
}
return mockActualRegistry[moduleName];
});
});
Personally, I've found this to be a useful construct, and now that the React hook issue is more prevalent, it might be good to have a version of this built in to Jest as something like a persistentModules
or excludeFromResetModules
option.
I had the same issue, instead of making any changes in setup files I end up with using https://jestjs.io/docs/en/jest-object.html#jestisolatemodulesfn in order to isolate each module so they are imported without having cache.
ex.
import { render } from '@testing-library/react';
import React from 'react';
const { getPlatform } = require('device');
jest.mock('device');
describe('', () => {
it('should render succesfully', () => {
getPlatform.mockImplementation(() => 'desktop');
jest.isolateModules(() => {
const { SomeComponent } = require('./SomeComponent');
const { container } = render(
<SomeComponent>test</SomeComponent>
);
expect(container).toMatchSnapshot();
});
});
});
I tried to debug Jest. I found that this issue is caused by this._moduleRegistry.clear();
in a resetModules
function. So, I remove that line and it works.
Not sure what happened inside a function. But hope this information helps someone that can fix this issue.
Thank you all so much. This was killing me all afternoon!
Thanks a lot, @quickgiant, your suggested solution https://github.com/facebook/jest/issues/8987#issuecomment-584898030 worked for us.
Most helpful comment
@kapral18 We are also experiencing the same issue. Have you found a work around for this?