Jest: La fábrica de módulos de `jest.mock()` no puede hacer referencia a ninguna variable fuera del alcance

Creado en 11 ene. 2017  ·  33Comentarios  ·  Fuente: facebook/jest

Estoy usando el fragmento de código de #1960 para simular Picker en RN

import React, {Component} from 'react';

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

Funciona bien en Jest 17, arroja el siguiente error en 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

Estoy usando React 15.4.2 y RN 0.40

Intenté babel-jest@test y se ejecutaron como se esperaba, pero todas mis instantáneas fallaron, parece que están llegando más accesorios que probablemente no estén relacionados con esto.

¿Hay algo que pueda hacer para solucionar esto ahora o debo esperar a la próxima versión por babel-jest ?

Gracias :)

Comentario más útil

Usar jest.doMock en lugar de jest.mock me ha ayudado.

Todos 33 comentarios

Usted necesita hacer ésto:

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

Esto solía ser un error que solucionamos. En un simulacro, solo puede requerir cosas localmente y no puede acceder a variables externas.

Para explicar por qué: con jest.resetModules() puede restablecer todos los módulos disponibles actualmente, de modo que cuando llame a require, obtendrá una nueva versión de cada módulo. Si usa React desde el nivel superior, terminará teniendo potencialmente dos copias de React.

Ah, ja, esa es la parte que no pude entender. ¡Gracias!

Este uso está bien y hay una escotilla de escape para él. Llame a su variable mockFoo .

Pero, si tengo múltiples simulacros:

jest.mock('./OfferList', () => 'OfferList');
jest.mock('./OfferHeader', () => 'OfferHeader');
jest.mock('./OfferHiredModal', () => 'OfferHiredModal');

¿Tengo que poner const React = require('React'); en cada simulacro?

si.

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

en caso de que alguien copie y pegue esto y vea que falla en CI (círculo/gitlab) y no en su local, asegúrese de que React esté en minúsculas react

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

@cpojer Quiero usar la variable __dirname , tampoco está permitido, ¿cómo puedo obtenerlo?
No quiero usar una ruta involucrada en el entorno, como /Users/xx/project

@cpojer Realmente no entiendo tu explicación:

Si usa React desde el nivel superior, terminará teniendo potencialmente dos copias de React.

Si necesito React localmente, también tendré dos copias de React local, ¿verdad?

Solo si llama a jest.resetModules() entre los dos requieren llamadas.

¿Cómo hace que esto funcione con los módulos ES6, que no se pueden poner dentro del alcance de la función?

puede usar la función import , junto con, por ejemplo, https://github.com/airbnb/babel-plugin-dynamic-import-node

@SimenB Gracias... ¿puedes dar un ejemplo? Estoy usando TypeScript que admite importaciones dinámicas, pero no tengo claro cómo funcionaría esto porque luego la implementación simulada se vuelve asíncrona, ¿Jest sabe cómo esperar a que se resuelva el simulacro antes de continuar con los casos de prueba?

Solo espera la promesa.

let myDep;

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

  myDep = await import('./some-modules.js');
})

No tengo idea de cómo se ve eso con mecanografiado, pero no debería ser muy diferente

Tengo problemas para burlarme de una función que usa la sintaxis de ES6 dentro de un ejemplo:

/**
 * @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);
  });
});

El operador de propagación (...) y la función generadora se transforman mediante una babel en algo que usa _extends y regeneratorRuntime consecuencia, a los que no se puede acceder:

babel-plugin-jest-hoist: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: _extends
    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, console, 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.

¿Alguien ha experimentado el problema antes? Yo uso la última broma.

Obteniendo el mismo error pero con:

Invalid variable access: _asyncToGenerator

Estoy usando babel-plugin-transform-regenerator. ¿Cómo puedo hacer que Broma no me queje de que "la fábrica de módulos de jest.mock() " no tiene "permiso para hacer referencia a ninguna variable fuera del alcance" en este caso?

// editar:
prueba completa es

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

Envolver algunas partes de la prueba en un IIFE asíncrono y eliminar el async delante de la función de prueba hace que no arroje el error:

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

Usar jest.doMock en lugar de jest.mock me ha ayudado.

Todavía no estoy del todo seguro, ya que hay otras cosas que fallan ahora (😄), pero parece que realmente ayuda, sí. ¡Gracias! 🙂

¿Alguna idea de por qué doMock funciona y mock no? Un poco extraño para mí también fue que si ponía la variable con el nombre "MockedComponent" recibía un error, pero cuando ponía "mockedComponent" no había ningún error, pero la referencia era "indefinida".

Un preprocesador mueve las llamadas 'jest.mock' de las llamadas 'it' al cierre externo y no funciona muy bien. Las llamadas 'jest.doMock' no se ven afectadas por un preprocesador.

Me encuentro con este problema cuando ejecuto jest con nodejs 10.0.0 , solo funciona la versión de nodo degradada .

@Soontao No puedo reproducir eso, ¿puede configurar una reproducción pequeña?

@SimenB
Gracias por su respuesta rápida, pero cuando intento reproducir eso con node v10 , descubrí que todas las pruebas funcionan bien, creo que el problema puede deberse a otras razones, y las perdí cuando reinstalé nodejs.

Mismo problema cuando se ejecuta con 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)

Eso no tiene nada que ver con el nodo 10, es solo que no tenemos console en la lista blanca. PR bienvenido! Realmente deberíamos usar algún módulo globals en lugar de una lista blanca manual...

Último arreglado aquí: #6075

Actualizar babel-jest con yarn add --dev babel-jest babel-core regenerator-runtime me solucionó este error.

Me topé con esto mientras buscaba en Google y parece que me he perdido esta línea crucial en el mensaje de error junto con todos los demás:

_Si se garantiza que el simulacro se requiere con pereza, se permiten nombres de variables con el prefijo mock ._

Simplemente cambie el nombre de lo que se está burlando para burlarse de YourComponentName

Me encontré con este problema después de agregar ese código en mi jest.conf, para agregar compatibilidad con tsx en las pruebas (sin ese código, no puedo escribir tsx en mis archivos spec.tsx:

  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 que tengo:

   babel-plugin-jest-hoist: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: _debounce
    Whitelisted objects: Array, ArrayBuffer, Boolean, DataView, Date, Error, EvalError, Float32Array, Float64Array, Function, Generator, GeneratorFunction, Infinity, Int16Arra
y, Int32Array, Int8Array, InternalError, Intl, JSON, Map, Math, NaN, Number, Object, Promise, Proxy, RangeError, ReferenceError, Reflect, RegExp, Set, String, Symbol, SyntaxEr
ror, TypeError, URIError, Uint16Array, Uint32Array, Uint8Array, Uint8ClampedArray, WeakMap, WeakSet, arguments, console, expect, isNaN, jest, parseFloat, parseInt, require, un
defined, DTRACE_NET_SERVER_CONNECTION, DTRACE_NET_STREAM_END, DTRACE_HTTP_SERVER_REQUEST, DTRACE_HTTP_SERVER_RESPONSE, DTRACE_HTTP_CLIENT_REQUEST, DTRACE_HTTP_CLIENT_RESPONSE,
 COUNTER_NET_SERVER_CONNECTION, COUNTER_NET_SERVER_CONNECTION_CLOSE, COUNTER_HTTP_SERVER_REQUEST, COUNTER_HTTP_SERVER_RESPONSE, COUNTER_HTTP_CLIENT_REQUEST, COUNTER_HTTP_CLIEN
T_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` (case inse
nsitive) are permitted.

Código en sí:

const DEBOUNCE_DELAY = 10
const _debounce = jest.requireActual('lodash-es/debounce').default
jest.mock('lodash-es/debounce', () =>
  jest.fn((fn) => _debounce(fn, DEBOUNCE_DELAY))
)

Tuve que reescribirlo con número mágico e importación en línea:

jest.mock('lodash-es/debounce', () =>
  jest.fn((fn) => jest.requireActual('lodash-es/debounce').default(fn, 10))
)

Tenga en cuenta que sin esa configuración en los globales ( 'ts-jest': { babelConfig: true } ) el código funcionó bien. Sin embargo, sin esa línea en la configuración, no pude ejecutar pruebas con tsx, me encontré con ese error:

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    C:\Users\Alend\vue-project\src\my-component\MyComponent.spec.tsx:20
                            render: function () { return <div id="foo">Foo</div>; }
                                                         ^

Algunas versiones de 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",

y la propia configuración de babel:

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 }]
  ]
}

Parece que ese problema todavía existe y ahora incluso las soluciones alternativas no ayudan a crear una aplicación de aplicación de reacción

`
ReferenceError: mockComponent no está definido

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

`

@khryshyn
Jest elevará automáticamente las llamadas jest.mock a la parte superior del módulo.
Es por eso que su constante mockComponent aún no está definida cuando se ejecuta jest.mock.

Para solucionar este "problema/característica", lo hago en 2 pasos como tales:

jest.mock('./components/Component', () => ({ Component: jest.fn() }));
import { Component } from "./components/Component";

Component.mockImplementation(() => <div>Mock</div>);

@khryshyn
Jest elevará automáticamente las llamadas jest.mock a la parte superior del módulo.
Es por eso que su constante mockComponent aún no está definida cuando se ejecuta jest.mock.

Para solucionar este "problema/característica", lo hago en 2 pasos como tales:

jest.mock('./components/Component', () => ({ Component: jest.fn() }));
import { Component } from "./components/Component";

Component.mockImplementation(() => <div>Mock</div>);

¿Es esto realmente correcto? Como @nckblu ya mencionó anteriormente, las variables que comienzan con 'mock' deberían estar disponibles como excepción. Y 'mockComponent' debería caer en esa excepción, ¿verdad?

Mientras tanto, si desea una solución alternativa para agregar una declaración de depuración, por ejemplo, console.log('Comprobando...'), prefije console.log con global para que funcione.

global.console.log('global console working')

¿Fue útil esta página
0 / 5 - 0 calificaciones