Jest: `jest.mock()`のモジュールファクトリは、スコープ外の変数を参照することを許可されていません

作成日 2017年01月11日  ·  33コメント  ·  ソース: facebook/jest

#1960のスニペットを使用してRNでPickerをモックしています

import React, {Component} from 'react';

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

Jest 17で正常に動作し、Jest18で次のエラーをスローします。

/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

React15.4.2とRN0.40を使用しています

babel-jest@testを試しましたが、期待どおりに実行されましたが、すべてのスナップショットが失敗しました。これとは関係のない、より多くの小道具が通過しているようです。

これを今すぐ修正するためにできることはありますか、それともbabel-jestの次のリリースを待つ必要がありますか?

ありがとう :)

最も参考になるコメント

$#$ jest.mock jest.doMockを使用することは私を助けました。

全てのコメント33件

これを行う必要があります:

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

これは、以前は修正したバグでした。 モックでは、ローカルでのみ要求でき、外部変数へのアクセスは許可されていません。

理由の説明: jest.resetModules()を使用すると、現在使用可能なすべてのモジュールをリセットできるため、requireを呼び出すと、各モジュールの新しいバージョンを取得できます。 トップレベルからReactを使用すると、Reactのコピーが2つになる可能性があります。

ああ、それは私が話すことができなかったビットです。 ありがとう!

この1つの使用法は問題なく、エスケープハッチがあります。 変数mockFooと呼びます。

しかし、私が複数のモックを持っている場合:

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

すべてのモックにconst React = require('React');を入れる必要がありますか?

はい。

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

誰かがこれをコピーして貼り付け、ローカルではなくCI(circle / gitlab)で失敗するのを見つけた場合は、 Reactが小文字のreactであることを確認してください

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

@cpojer __dirname変数を使用したいのですが、これも許可されていません。どうすれば取得できますか?
/Users/xx/projectのような環境に関係するパスを使用したくない

@cpojer私はあなたの説明を本当に理解していません:

トップレベルからReactを使用すると、Reactのコピーが2つになる可能性があります。

ローカルでReactが必要な場合は、ローカルReactのコピーも2つありますよね?

2つのrequire呼び出しの間にjest.resetModules()を呼び出す場合のみ。

関数スコープ内に配置できないES6モジュールでこれをどのように機能させますか?

import関数を、たとえばhttps://github.com/airbnb/babel-plugin-dynamic-import-nodeと一緒に使用できます。

@SimenBありがとう...例を挙げていただけますか? 動的インポートをサポートするTypeScriptを使用していますが、モックの実装が非同期になるため、これがどのように機能するかわかりません。Jestは、テストケースを続行する前にモックが解決するのを待つ方法を知っていますか?

約束を待つだけです。

let myDep;

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

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

それがtypescriptでどのように見えるかはわかりませんが、それほど違いはありません

例の中でES6構文を使用する関数をモックするのに問題があります。

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

スプレッド演算子(...)とジェネレーター関数は、バベルによって_extendsregeneratorRuntimeを使用して、アクセスできないものに変換されます。

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.

誰かが以前に問題を経験したことがありますか? 最新の冗談を使用しています。

同じエラーが発生しますが、次のようになります。

Invalid variable access: _asyncToGenerator

私はbabel-plugin-transform-regeneratorを使用しています。 この場合、「 jest.mock()のモジュールファクトリ」が「スコープ外の変数を参照することを許可されていない」ことについて文句を言わないようにするにはどうすればよいですか?!

// 編集:
完全なテストは

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

テストの一部を非同期IIFEでラップし、テスト関数の前にあるasyncを削除すると、jestはエラーをスローしません。

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

$#$ jest.mock jest.doMockを使用することは私を助けました。

現在失敗しているものが他にもあるので(😄)、まだ完全にはわかりませんが、それは本当に役立つようです、はい。 ありがとう! 🙂

doMockが機能し、 mockが機能しない理由はありますか? また、「MockedComponent」という名前の変数を入力するとエラーが発生しましたが、「mockedComponent」を入力するとエラーは発生しませんでしたが、参照は「undefined」でした。

'jest.mock'呼び出しは、 'it'呼び出しからプリプロセッサーによって外部クロージャーに移動され、うまく機能しません。 'jest.doMock'呼び出しは、プリプロセッサの影響を受けません。

nodejs 10.0.0でjestを実行すると、この問題が発生します。ダウングレードされたノードバージョンだけが機能します。

@Soontao再現できませんが、小さな再現を設定できますか?

@SimenB
迅速な返信ありがとうございますが、 node v10で再現しようとすると、すべてのテストが正常に機能することがわかりました。問題は他の理由で発生した可能性があり、nodejsを再インストールすると失われました。

nodejs10.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)

これはノード10とは何の関係もありません。ホワイトリストにconsoleがないだけです。 PRようこそ! 手動のホワイトリストではなく、 globalsモジュールを使用する必要があります...

ここで修正された最後のもの:#6075

yarn add --dev babel-jest babel-core regenerator-runtimeでbabel-jestをアップグレードすると、このエラーが修正されました。

グーグル中にこれに遭遇しましたが、他のすべての人と一緒にエラーメッセージのこの重要な行を見逃したようです。

_モックが怠惰に必要であることが確実な場合は、 mockで始まる変数名が許可されます。_

モックしているものの名​​前をYourComponentNameをモックするように変更するだけです

jest.confにそのコードを追加した後、テストでtsxサポートを追加するためにこの問題が発生しました(そのコードがないと、spec.tsxファイルに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
    }
  }
}

私が得たエラー:

   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.

コード自体:

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

マジックナンバーとインラインインポートで書き直さなければなりませんでした。

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

グローバル( 'ts-jest': { babelConfig: true } )コードにその設定がなくても、正常に機能したことに注意してください。 ただし、構成にその行がないと、tsxでテストを実行できず、次のエラーが発生しました。

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

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",

および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 }]
  ]
}

そのような問題はまだ存在しているようで、今では回避策でさえ反応アプリアプリケーションの作成に役立ちません

`
ReferenceError:mockComponentが定義されていません

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

`

@khryshyn
Jestは、jest.mock呼び出しをモジュールの先頭に自動的に引き上げます。
そのため、jest.mockの実行時にmockComponent constがまだ定義されていません。

この「問題/機能」を回避するために、私は2つのステップでそれを行います。

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

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

@khryshyn
Jestは、jest.mock呼び出しをモジュールの先頭に自動的に引き上げます。
そのため、jest.mockの実行時にmockComponent constがまだ定義されていません。

この「問題/機能」を回避するために、私は2つのステップでそれを行います。

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

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

これは本当に正しいですか? @nckbluがすでに述べたように、 'mock'で始まる変数は例外として使用可能である必要があります。 そして、「mockComponent」はその例外に該当するはずですよね?

それまでの間、console.log( 'Checking ...')などのデバッグステートメントを追加する回避策が必要な場合は、console.logの前にglobalを付けて機能させます。

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

このページは役に立ちましたか?
0 / 5 - 0 評価