jest: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables

I’m using the snippet from #1960 to mock Picker in RN

import React, {Component} from 'react';

jest.mock(`Picker`, () => {
 // ...etc
});

Works fine in Jest 17, throws following error in Jest 18:

/Users/simonsmith/Sites/new-look/newlookapp/test/unit/setup.js: babel-plugin-jest-hoist: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: React
    Whitelisted objects: Array, ArrayBuffer, ..... etc

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 😃

About this issue

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

Commits related to this issue

Most upvoted comments

Using jest.doMock instead of jest.mock has helped me.

you need to do this:

jest.mock(`Picker`, () => {
  const React = require('react');
});

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.

yes.

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 mock are permitted.

Just change the name of what you’re mocking to mockYourComponentName

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 React is a lowercase react

jest.mock(`Picker`, () => {
  const React = require('react');
});

Upgrading babel-jest with yarn add --dev babel-jest babel-core regenerator-runtime fixed this error for me.

To explain why: With jest.resetModules() you may reset all currently available modules, so when you call require, you’ll get a new version of each module. If you use React from the top level, you’ll end up having potentially two copies of React.

Same issue when run with nodejs 10.0.0

 /xxx/node_modules/react-native/jest/setup.js: babel-plugin-jest-hoist: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: console
    Whitelisted objects: Array, ArrayBuffer, Boolean, DataView, Date, Error, EvalError, Float32Array, Float64Array, Function, Generator, GeneratorFunction, Infinity, Int16Array, Int32Array, Int8Array, InternalError, Intl, JSON, Map, Math, NaN, Number, Object, Promise, Proxy, RangeError, ReferenceError, Reflect, RegExp, Set, String, Symbol, SyntaxError, TypeError, URIError, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, WeakMap, WeakSet, arguments, expect, jest, require, undefined, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE, global, process, Buffer, clearImmediate, clearInterval, clearTimeout, setImmediate, setInterval, setTimeout.
    Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` are permitted.

      at invariant (node_modules/babel-plugin-jest-hoist/build/index.js:14:11)
      at newFn (node_modules/babel-traverse/lib/visitors.js:276:21)
      at NodePath._call (node_modules/babel-traverse/lib/path/context.js:76:18)
      at NodePath.call (node_modules/babel-traverse/lib/path/context.js:48:17)
      at NodePath.visit (node_modules/babel-traverse/lib/path/context.js:105:12)
      at TraversalContext.visitQueue (node_modules/babel-traverse/lib/context.js:150:16)

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 nodejs 10.0.0, just downgraded node version is work.

This one usage is ok and there is an escape hatch for it. Call your variable mockFoo.

@khryshyn Jest will automatically hoist jest.mock calls to the top of the module. That’s why your mockComponent const is not defined yet when jest.mock runs.

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?

@khryshyn Jest will automatically hoist jest.mock calls to the top of the module. That’s why your mockComponent const is not defined yet when jest.mock runs.

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>);

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

Seems like such issue still exist and now even workarounds don’t help in create react app application

` ReferenceError: mockComponent is not defined

  17 | const mockComponent = () => <div>Mock</div>;
  18 | 
> 19 | jest.mock('./components/Component', () => ({ Component: mockComponent }));

`

Any idea why doMock works and mock does not? Weird bit for me was also that if I put the variable with name “MockedComponent” I received an error, but when I put “mockedComponent” there was no error, but the reference was “undefined”.

@cpojer I want to use __dirname variable, it is also not allowed, how can I get it?
I don’t want to use a environment involved path, like /Users/xx/project

That doesn’t have anything to do with node 10, it’s just that we don’t have console in the whitelist. PR welcome! We really should just use some globals module instead of a manual whitelist…

How do you make this work with ES6 modules, which cannot be put inside the function scope?

@ghost23 I think the mock prefix exception is only valid when the module factory that returns the mock is a higher-order-function (HOF), and the mock-prefixed variable is referenced at the inner layer. That’s why this technique is generally used to mock classes because classes have constructors which are HOFs naturally.

This is mostly covered in calling-jestmock-with-the-module-factory-parameter. Quoted as below for your quick reference.

In order to mock a constructor function, the module factory must return a constructor function. In other words, the module factory must be a function that returns a function - a higher-order function (HOF).

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.

Last one fixed here: #6075

Only if you call jest.resetModules() between the two require calls.