使用 Jest 模拟的服务会导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误

问题描述 投票:0回答:7

我正在尝试模拟对服务的调用,但我遇到了以下消息:

jest.mock()
的模块工厂不允许引用任何超出范围的变量

我正在使用带有 ES6 语法、笑话和酶的 babel。

我有一个名为

Vocabulary
的简单组件,它从
VocabularyEntry
获取
vocabularyService
对象列表并渲染它。

import React from 'react';
import vocabularyService from '../services/vocabularyService';

export default class Vocabulary extends React.Component {
    render() {

        let rows = vocabularyService.vocabulary.map((v, i) => <tr key={ i } >
            <td>{ v.src }</td>
            <td>{ v.target }</td>
        </tr>);
        // render rows
    }
}

vocabularyServise
非常简单:

import { VocabularyEntry } from '../model/VocabularyEntry';

class VocabularyService {

    constructor() {
        this.vocabulary = [new VocabularyEntry("a", "b")];
    }
}
export default new VocabularyService();

现在我想在测试中模拟

vocabularyService

import { shallow } from 'enzyme';
import React from 'react';
import Vocabulary from "../../../src/components/Vocabulary ";
import { VocabularyEntry } from '../../../src/model/VocabularyEntry'

jest.mock('../../../src/services/vocabularyService', () => ({

    vocabulary: [new VocabularyEntry("a", "a1")]

}));

describe("Vocabulary tests", () => {

    test("renders the vocabulary", () => {

        let $component = shallow(<Vocabulary/>);

        // expect something

    });
});

运行测试会导致错误: Vocabulary.spec.js: babel-plugin-jest-hoist:

jest.mock()
的模块工厂不允许引用任何超出范围的变量。 无效的变量访问:VocabularyEntry。

据我了解,我无法使用 VocabularyEntry,因为它没有声明(因为笑话将模拟定义移动到文件顶部)。

任何人都可以解释一下我该如何解决这个问题吗?我看到了需要模拟调用中的引用的解决方案,但我不明白如何使用类文件来做到这一点。

unit-testing reactjs babeljs jestjs
7个回答
140
投票

您需要将模拟组件存储在名称前缀为“mock”的变量中。 该解决方案基于我收到的错误消息末尾的注释。

注意:这是防止未初始化的模拟的预防措施 变量。如果保证mock是惰性需要的,则变量 允许使用带有

mock
前缀的名称。

import {shallow} from 'enzyme';
import React from 'react';
import Vocabulary from "../../../src/components/Vocabulary ";
import {VocabularyEntry} from '../../../src/model/VocabularyEntry'

const mockVocabulary = () => new VocabularyEntry("a", "a1");

jest.mock('../../../src/services/vocabularyService', () => ({
    default: mockVocabulary
}));

describe("Vocabulary tests", () => {

test("renders the vocabulary", () => {

    let $component = shallow(<Vocabulary/>);

    // expect something

});

71
投票

问题是所有

jest.mock
在编译时都会被提升到实际代码块的顶部,在本例中是文件的顶部。此时
VocabularyEntry
尚未导入。您可以将
mock
放入测试中的
beforeAll
块中,或者像这样使用
jest.mock

import {shallow} from 'enzyme';
import React from 'react';
import Vocabulary from "../../../src/components/Vocabulary ";
import {VocabularyEntry} from '../../../src/model/VocabularyEntry'
import vocabularyService from '../../../src/services/vocabularyService'

jest.mock('../../../src/services/vocabularyService', () => jest.fn())

vocabularyService.mockImplementation(() => ({
  vocabulary: [new VocabularyEntry("a", "a1")]
}))

这将首先使用一个简单的间谍来模拟模块,在导入所有内容后,它将设置模拟的真正实现。


58
投票

如果您在升级到较新的 Jest 时遇到类似的错误(我的例子是 19 到 21),您可以尝试将

jest.mock
更改为
jest.doMock

在这里找到了这个 – https://github.com/facebook/jest/commit/6a8c7fb874790ded06f4790fdb33d8416a7284c8


9
投票
jest.mock("../../../src/services/vocabularyService", () => {
  // eslint-disable-next-line global-require
  const VocabularyEntry = require("../../../src/model/VocabularyEntry");

  return {
    vocabulary: [new VocabularyEntry("a", "a1")]
  };
});

我认为它也应该适用于动态导入,而不是

require
,但没能成功。


5
投票

在使用

jest.fn()
模拟的模块中实现
jest.mock
时,请确保执行以下两个步骤:

  1. 变量名称应以
    mock
    开头。
  2. 如果您写了类似
    coolFunction: mockCoolFunction
    的内容,请务必将其更改为
    coolFunction: (...args) => mockCoolFunction(...args)
    。这样,只要您没有运行该函数,Jest 就不需要知道
    mockCoolFunction
    是什么。

欲了解更多信息,我推荐这篇博文


2
投票

对于我从事的一个项目,我尝试了此处列出的所有解决方案,但没有一个有效。

我是这样嘲笑模块的:

import { myModuleFn } from 'myModule';

jest.mock('myModule', () => {
  return {
    __esModule: true,
    ...jest.requireActual('myModule'),
    myModuleFn: jest.fn(),
  };
});

使用

Object.assign
而不是展开运算符修复了错误:

jest.mock('myModule', () => {
  return Object.assign({}, jest.requireActual('myModule'), {
    __esModule: true,
    myModuleFn: jest.fn(),
  })
});

我怀疑这与项目特定的笑话或编译器配置有关。


0
投票

就我而言,这个问题是在我使用react-native-git-upgrade将react-native项目升级到v0.61后开始的。

在我尝试了所有我能做的事情之后。我决定清理项目并让所有测试恢复工作。

# react-native-clean-project

但是在运行react-native-clean-project时要小心,它会清除所有ios和android文件夹,包括本机代码,所以在出现提示时只需回答N即可。就我而言,我只是选择擦除 node_modules 文件夹。

© www.soinside.com 2019 - 2024. All rights reserved.