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

IT技术 unit-testing reactjs babeljs jestjs
2021-04-22 17:35:00

我正在尝试模拟对服务的调用,但我正在努力处理以下消息:The module factory of jest.mock()is not allowed to reference any out-of-scope variables

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

我有一个简单的组件Vocabulary,它VocabularyEntry从 a获取-Objects列表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
 }
 }

vocabularyServiseIST很简单:

  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: The module factory jest.mock()is not allowed to reference any out-of-scope variables。无效的变量访问:VocabularyEntry。

据我了解,我不能使用 VocabularyEntry,因为它没有声明(因为 jest 将模拟定义移动到文件的顶部)。

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

5个回答

您需要将模拟组件存储在名称以“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

});
这会产生错误: Cannot access 'mockVocabulary' before initialization
2021-05-26 17:35:00

问题是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")]
}))

这将首先用一个简单的 spy 模拟module,在导入所有内容后,它会设置模拟的真实实现。

当我这样做时,我得到myService.mockImplementation is not a function.
2021-05-22 17:35:00
这似乎是一个很好的方法!我试过这个,但是当Vocabulary尝试调用时会导致错误,vocabularyService.vocabulary.map因为vocabularyService.vocabulary未定义。我注销了vocabularyService,这是一个笑话模拟功能。也许这就是问题所在?
2021-05-30 17:35:00
不适合我 :( 请参阅stackoverflow.com/questions/45771844/...
2021-06-10 17:35:00
啊对不起,我的代码中有一个错误,mockImplementation需要返回一个函数而不仅仅是对象。
2021-06-12 17:35:00
这根本行不通。移动jest.mockbeforeAll给出相同的错误并使用mockImplementation给出TypeError: mockedModule.mockedFunction is not a function
2021-06-21 17:35:00

如果您在升级到较新的 Jest 时遇到类似的错误 [在我的情况下为 19 到 21],您可以尝试更改jest.mockjest.doMock.

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

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

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

我认为它也应该与动态导入一起工作,而不是require但没有设法让它工作。

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

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

# react-native-clean-project

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