-
-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The module factory of jest.mock()
is not allowed to reference any out-of-scope variables
#2567
Comments
you need to do this:
This used to be a bug that we fixed. In a mock you can only require things locally and you aren't allowed to access external variables. |
To explain why: With |
Ah ha, that's the bit I couldn't suss. Thanks! |
This one usage is ok and there is an escape hatch for it. Call your variable |
But, If I have multiple mocks: jest.mock('./OfferList', () => 'OfferList');
jest.mock('./OfferHeader', () => 'OfferHeader');
jest.mock('./OfferHiredModal', () => 'OfferHiredModal'); Do I have to put |
yes. |
based on FB GitHub trace jestjs/jest#2567 you can't reference external data inside of mock function. You will get following error The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables. To fix this you need to declare local variable instead
jest.mock(`Picker`, () => {
const React = require('React');
}); in case anyone copy pastes this and sees it failing in CI (circle/gitlab) and not their local, make sure jest.mock(`Picker`, () => {
const React = require('react');
}); |
@cpojer I want to use |
@cpojer I don't really understand your explanation:
If I require React locally, I will also have two copies of local React, right? |
Only if you call |
How do you make this work with ES6 modules, which cannot be put inside the function scope? |
you can use the |
@SimenB Thanks... can you give an example? I'm using TypeScript which supports dynamic imports but I'm not clear how this would work because then the mock implementation becomes async, does Jest know how to wait for the mock to resolve before continuing with test cases? |
Just await the promise. let myDep;
beforeEach(async () => {
jest.resetModules();
myDep = await import('./some-modules.js');
}) No idea how that looks with typescript, but shouldn't be too different |
I am having trouble mocking with a function using ES6 syntax inside an example: /**
* @jest-environment jsdom
*/
import SagaTester from "redux-saga-tester";
import supportRequests from "sagas/support_requests";
import reducers from "reducers";
import { fetched } from "actions/support_requests";
describe("success", () => {
it("sends the message via connection", async () => {
const sagaTester = new SagaTester({
reducers: reducers,
initialState: {
router: { location: { pathname: "/support_requests" } },
supportRequests: null,
authenticated: true,
currentUser: { isSupportAgent: true },
},
});
jest.mock("sagas/oauth", () => {
return {
...require.requireActual("sagas/oauth"),
callFetch: function* () {
return { ok: true, json: () => Promise.resolve({ support_requests: [], meta: { pagination: {} } }) };
},
};
});
sagaTester.start(supportRequests);
await sagaTester.waitFor(fetched);
});
}); The spread operator (...) and generator function get transformed by a babel into something using
Has anyone experienced the issue before? I use latest jest. |
Getting the same error but with:
I'm using the babel-plugin-transform-regenerator. How can I get jest to not complain about "The module factory of // edit: it('should request data via API', async () => {
const store = mockStore({ fields: initialState });
jest.resetModules();
jest.mock('services/api-client', () => ({
getFieldsByFarm: async () => {
return [{ field: {} }];
},
}));
const Actions = require('./actions');
const expected = [
{ type: 'FIELDS/REQUEST' },
{ type: 'FIELDS/RECEIVE', payload: { items: [{ field: {} }], didInvalidate: false },},
];
await store.dispatch(Actions.getFieldsByFarm());
const dispatchedActions = store.getActions();
expect(dispatchedActions[0]).toEqual(expected[0]);
expect(dispatchedActions[1].payload).toEqual(expect.objectContaining(expected[1].payload));
}); Wrapping some parts of the test in an async IIFE and removing the it('should request data via API', (done) => {
const store = mockStore({ fields: initialState });
jest.resetModules();
jest.mock('services/api-clients/nana', () => ({
getFieldsByFarm: async () => {
return [{ field: {} }];
},
}));
const Actions = require('./actions');
(async () => {
const expected = [
{ type: 'FIELDS/REQUEST' },
{
type: 'FIELDS/RECEIVE',
payload: { items: [{ field: {} }], didInvalidate: false },
},
];
await store.dispatch(Actions.getFieldsByFarm());
const dispatchedActions = store.getActions();
expect(dispatchedActions[0]).toEqual(expected[0]);
expect(dispatchedActions[1].payload).toEqual(expect.objectContaining(expected[1].payload));
done();
})();
}); |
Using |
Not entirely sure yet since there are other things failing now ( 😄 ) but looks like it really helps, yes. Thanks! 🙂 |
Any idea why |
The ‘jest.mock’ calls get moved from ‘it’ calls to the outer closure by a preprocessor and it does not work very well. ‘jest.doMock’ calls aren’t affected by a preprocessor. |
I meet this problem when I run jest with |
@Soontao I cannot reproduce that, are you able to set up a small reproduction? |
@SimenB |
Same issue when run with nodejs 10.0.0
|
That doesn't have anything to do with node 10, it's just that we don't have |
Upgrading babel-jest with |
I just stumbled upon this while googling and it seems like I've missed this crucial line in the error message along with everyone else: If it is ensured that the mock is required lazily, variable names prefixed with Just change the name of what you're mocking to mockYourComponentName |
This helps prepare for using Babel to transpile TypeScript but is good practice regardless. - In `code_intelligence.test.tsx`, the `jest.mock('react-dom', ...)` was rejected with a fatal error by Babel (in babel-jest) because it referred to an out-of-scope variable (for why it rejects this, see jestjs/jest#2567). Also, it was undefined behavior that the `jest.mock` of `react-dom` applied to other modules (see "...mocked only for the file that calls `jest.mock`..." at https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options). So, this commit passes the `react-dom` `render` function as an argument to the functions under test to make mocking easier and fully explicit without the need for test harness magic. - Removes unnecessary mocking of `react-dom` in `text_fields.test.tsx`. The mock was never checked.
) This helps prepare for using Babel to transpile TypeScript but is good practice regardless. - In `code_intelligence.test.tsx`, the `jest.mock('react-dom', ...)` was rejected with a fatal error by Babel (in babel-jest) because it referred to an out-of-scope variable (for why it rejects this, see jestjs/jest#2567). Also, it was undefined behavior that the `jest.mock` of `react-dom` applied to other modules (see "...mocked only for the file that calls `jest.mock`..." at https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options). So, this commit passes the `react-dom` `render` function as an argument to the functions under test to make mocking easier and fully explicit without the need for test harness magic. - Removes unnecessary mocking of `react-dom` in `text_fields.test.tsx`. The mock was never checked.
…) (#55) * Updated react native dependencies. * Updated babel dependencies. Removed the caret from react and react-native beacause of incorrect peer dependencies. * Changed from .babelrc to babel.config.js (based on facebook/react-native#21075). * Add mock prefix to allow out of scope referencing (jestjs/jest#2567) * Mock the async-storage module. * Added explanation to README. * Changed version to 2.0.0 as it is a breaking change.
I run into this issue after I add that code in my jest.conf, to add tsx support in tests (without that code, I can't write tsx in my spec.tsx files: globals: {
jasmine: true,
+ 'ts-jest': {
+ babelConfig: true
+ }
} module.exports = {
// eslint-disable-next-line no-undef
rootDir: path.resolve(__dirname, '../'),
roots: ['<rootDir>/src'],
verbose: false,
moduleFileExtensions: ['ts', 'tsx', 'vue', 'js', 'jsx', 'json'],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts|js)x?$',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.(js|jsx)?$': 'babel-jest',
'^.+\\.tsx?$': 'ts-jest'
},
transformIgnorePatterns: ['<rootDir>/node_modules/(?!lodash-es)'],
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
setupFilesAfterEnv: ['<rootDir>/test/jest.init.ts'],
// run tests with --coverage to see coverage
coverageDirectory: '<rootDir>/test/coverage',
coverageReporters: ['html', 'text-summary'],
collectCoverageFrom: ['src/**/*.{ts,tsx,js,jsx,vue}', '!**/node_modules/**'],
globals: {
jasmine: true,
'ts-jest': {
babelConfig: true
}
}
} Error I got:
Code itself: const DEBOUNCE_DELAY = 10
const _debounce = jest.requireActual('lodash-es/debounce').default
jest.mock('lodash-es/debounce', () =>
jest.fn((fn) => _debounce(fn, DEBOUNCE_DELAY))
) I had to rewrite it with magic number and inline import: jest.mock('lodash-es/debounce', () =>
jest.fn((fn) => jest.requireActual('lodash-es/debounce').default(fn, 10))
) Notice, that without that config in globals (
Some versions from package.json: "@babel/core": "^7.4.5",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "24.8.0",
"jest": "24.8.0",
"ts-jest": "24.0.2", and the babel config itself: module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: 'commonjs',
targets: {
browsers: ['> 1%', 'last 2 versions', 'not ie <= 11']
}
}
],
'@vue/babel-preset-jsx'
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-function-sent',
'@babel/plugin-proposal-json-strings',
'@babel/plugin-proposal-numeric-separator',
'@babel/plugin-proposal-throw-expressions',
'@babel/plugin-syntax-dynamic-import',
['@babel/plugin-transform-runtime', { corejs: 2 }],
['@babel/plugin-proposal-decorators', { legacy: true }]
]
} |
Seems like such issue still exist and now even workarounds don't help in create react app application `
` |
@khryshyn To go around this "issue/feature", I do it in 2 steps as such: jest.mock('./components/Component', () => ({ Component: jest.fn() }));
import { Component } from "./components/Component";
Component.mockImplementation(() => <div>Mock</div>); |
Is this really correct? As @nckblu already mentioned above, variables that start with 'mock' should be available as an exception. And 'mockComponent' should fall into that exception, right? |
In the meantime, if you want a workaround to add a debug statement e.g. console.log('Checking...'), prefix console.log with global to make it work.
|
hi, i have this problem too |
@alisajadih I am facing same , have you found solution for it? |
@ghost23 I think the This is mostly covered in calling-jestmock-with-the-module-factory-parameter. Quoted as below for your quick reference.
With newer versions of node (14+), you could hit a different error message as shown in #10996. The mockimplementation approach mentioned by @maxletourneur is a nice solution. |
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
I'm using the snippet from #1960 to mock
Picker
in RNWorks fine in Jest 17, throws following error in Jest 18:
I'm using React 15.4.2 and RN 0.40
I tried
babel-jest@test
and they run as expected but all my snapshots fail, looks like more props are coming through which is probably unrelated to this.Anything I can do to fix this now or should I wait for the next release for
babel-jest
?Thanks :)
The text was updated successfully, but these errors were encountered: