Jest: Pabrik modul `jest.mock()` tidak diizinkan untuk mereferensikan variabel di luar cakupan apa pun

Dibuat pada 11 Jan 2017  ·  33Komentar  ·  Sumber: facebook/jest

Saya menggunakan cuplikan dari #1960 untuk mengejek Picker di RN

import React, {Component} from 'react';

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

Bekerja dengan baik di Jest 17, melempar kesalahan berikut di 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

Saya menggunakan React 15.4.2 dan RN 0.40

Saya mencoba babel-jest@test dan mereka berjalan seperti yang diharapkan tetapi semua snapshot saya gagal, sepertinya lebih banyak alat peraga datang yang mungkin tidak terkait dengan ini.

Adakah yang bisa saya lakukan untuk memperbaikinya sekarang atau haruskah saya menunggu rilis berikutnya untuk babel-jest ?

Terima kasih :)

Komentar yang paling membantu

Menggunakan jest.doMock alih-alih jest.mock telah membantu saya.

Semua 33 komentar

Anda perlu melakukan ini:

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

Ini dulunya adalah bug yang kami perbaiki. Dalam tiruan Anda hanya dapat meminta hal-hal secara lokal dan Anda tidak diizinkan untuk mengakses variabel eksternal.

Untuk menjelaskan alasannya: Dengan jest.resetModules() Anda dapat mengatur ulang semua modul yang tersedia saat ini, jadi ketika Anda memanggil require, Anda akan mendapatkan versi baru dari setiap modul. Jika Anda menggunakan React dari tingkat atas, Anda akan berpotensi memiliki dua salinan React.

Ah ha, itu sedikit yang tidak bisa saya permasalahkan. Terima kasih!

Penggunaan yang satu ini baik-baik saja dan ada pintu keluar untuk itu. Panggil variabel Anda mockFoo .

Tetapi, Jika saya memiliki banyak ejekan:

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

Apakah saya harus memasukkan const React = require('React'); di setiap tiruan?

Ya.

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

jika ada yang menyalin tempel ini dan melihatnya gagal di CI (lingkaran/gitlab) dan bukan lokal mereka, pastikan React adalah huruf kecil react

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

@cpojer Saya ingin menggunakan variabel __dirname juga tidak diperbolehkan, bagaimana saya bisa mendapatkannya?
Saya tidak ingin menggunakan jalur yang melibatkan lingkungan, seperti /Users/xx/project

@cpojer Saya tidak begitu mengerti penjelasan Anda:

Jika Anda menggunakan React dari tingkat atas, Anda akan berpotensi memiliki dua salinan React.

Jika saya memerlukan React lokal, saya juga akan memiliki dua salinan React lokal, bukan?

Hanya jika Anda menelepon jest.resetModules() di antara keduanya memerlukan panggilan.

Bagaimana Anda membuatnya berfungsi dengan modul ES6, yang tidak dapat dimasukkan ke dalam lingkup fungsi?

anda dapat menggunakan fungsi import , bersama dengan misalnya https://github.com/airbnb/babel-plugin-dynamic-import-node

@SimenB Terima kasih... dapatkah anda memberikan contoh? Saya menggunakan TypeScript yang mendukung impor dinamis tetapi saya tidak jelas bagaimana ini akan bekerja karena kemudian implementasi tiruan menjadi asinkron, apakah Jest tahu bagaimana menunggu tiruan diselesaikan sebelum melanjutkan dengan kasus uji?

Tunggu saja janjinya.

let myDep;

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

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

Tidak tahu bagaimana tampilannya dengan TypeScript, tetapi seharusnya tidak terlalu berbeda

Saya mengalami masalah mengejek dengan fungsi menggunakan sintaks ES6 di dalam contoh:

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

Operator spread (...) dan fungsi generator diubah oleh babel menjadi sesuatu menggunakan _extends dan regeneratorRuntime yang tidak dapat diakses:

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.

Adakah yang pernah mengalami masalah ini sebelumnya? Saya menggunakan lelucon terbaru.

Mendapatkan kesalahan yang sama tetapi dengan:

Invalid variable access: _asyncToGenerator

Saya menggunakan babel-plugin-transform-regenerator. Bagaimana saya bisa bercanda untuk tidak mengeluh tentang "Pabrik modul jest.mock() " tidak "diizinkan untuk mereferensikan variabel di luar cakupan" dalam kasus ini?!

// edit:
Tes penuh adalah

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

Membungkus beberapa bagian tes dalam IIFE asinkron dan menghapus async di depan fungsi tes membuat lelucon tidak membuang kesalahan:

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

Menggunakan jest.doMock alih-alih jest.mock telah membantu saya.

Belum sepenuhnya yakin karena ada hal lain yang gagal sekarang ( ) tetapi sepertinya itu sangat membantu, ya. Terima kasih! 🙂.

Adakah yang tahu mengapa doMock berhasil dan mock tidak? Sedikit aneh bagi saya juga bahwa jika saya meletakkan variabel dengan nama "MockedComponent" saya menerima kesalahan, tetapi ketika saya meletakkan "mockedComponent" tidak ada kesalahan, tetapi referensi "tidak terdefinisi".

Panggilan 'jest.mock' dipindahkan dari panggilan 'itu' ke penutupan luar oleh preprosesor dan itu tidak berfungsi dengan baik. Panggilan 'jest.doMock' tidak terpengaruh oleh praprosesor.

Saya menemui masalah ini ketika saya menjalankan lelucon dengan nodejs 10.0.0 , hanya versi simpul yang diturunkan berfungsi.

@Soontao Saya tidak dapat mereproduksi itu, apakah Anda dapat mengatur reproduksi kecil?

@SimenB
Terima kasih atas balasan Anda yang cepat, tetapi ketika saya mencoba mereproduksinya dengan node v10 , saya menemukan bahwa semua tes berfungsi dengan baik, saya pikir masalahnya mungkin disebabkan oleh alasan lain, dan saya kehilangannya ketika saya menginstal ulang nodejs.

Masalah yang sama saat dijalankan dengan 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)

Itu tidak ada hubungannya dengan node 10, hanya saja kita tidak memiliki console dalam daftar putih. PR selamat datang! Kami benar-benar harus menggunakan beberapa modul globals alih-alih daftar putih manual...

Yang terakhir diperbaiki di sini: #6075

Memutakhirkan babel-jest dengan yarn add --dev babel-jest babel-core regenerator-runtime memperbaiki kesalahan ini untuk saya.

Saya baru saja menemukan ini saat mencari di Google dan sepertinya saya melewatkan baris penting ini dalam pesan kesalahan bersama dengan orang lain:

_Jika dipastikan bahwa mock diperlukan dengan malas, nama variabel yang diawali dengan mock diperbolehkan._

Ubah saja nama yang Anda olok-olok untuk mengejek YourComponentName

Saya mengalami masalah ini setelah saya menambahkan kode itu di jest.conf saya, untuk menambahkan dukungan tsx dalam pengujian (tanpa kode itu, saya tidak dapat menulis tsx di file spec.tsx saya:

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

Kesalahan yang saya dapatkan:

   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.

Kode itu sendiri:

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

Saya harus menulis ulang dengan nomor ajaib dan impor sebaris:

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

Perhatikan, bahwa tanpa konfigurasi itu dalam kode globals ( 'ts-jest': { babelConfig: true } ) berfungsi dengan baik. Namun tanpa baris itu di konfigurasi saya tidak dapat menjalankan tes dengan tsx, saya menghadapi kesalahan itu:

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

Beberapa versi dari 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",

dan konfigurasi babel itu sendiri:

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

Sepertinya masalah seperti itu masih ada dan sekarang bahkan solusi tidak membantu dalam membuat aplikasi aplikasi reaksi

`
ReferenceError: mockComponent tidak ditentukan

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

`

@khryshyn
Jest akan secara otomatis mengangkat panggilan jest.mock ke bagian atas modul.
Itu sebabnya const mockComponent Anda belum ditentukan saat jest.mock dijalankan.

Untuk mengatasi "masalah/fitur" ini, saya melakukannya dalam 2 langkah seperti:

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

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

@khryshyn
Jest akan secara otomatis mengangkat panggilan jest.mock ke bagian atas modul.
Itu sebabnya const mockComponent Anda belum ditentukan saat jest.mock dijalankan.

Untuk mengatasi "masalah/fitur" ini, saya melakukannya dalam 2 langkah seperti:

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

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

Apakah ini benar? Seperti yang sudah disebutkan @nckblu di atas, variabel yang dimulai dengan 'mock' harus tersedia sebagai pengecualian. Dan 'mockComponent' harus termasuk dalam pengecualian itu, bukan?

Sementara itu, jika Anda ingin solusi untuk menambahkan pernyataan debug misalnya console.log('Checking...'), awalan console.log dengan global untuk membuatnya bekerja.

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

Apakah halaman ini membantu?
0 / 5 - 0 peringkat