【问题标题】:Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误
【发布时间】:2017-11-22 19:11:13
【问题描述】:

我正在尝试模拟对服务的调用,但遇到以下消息:jest.mock() 的模块工厂不允许引用任何范围外的变量 .

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

我有一个名为Vocabulary 的简单组件,它从vocabularyService 获取VocabularyEntry-Objects 列表并呈现它。

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

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

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

【问题讨论】:

    标签: unit-testing reactjs babeljs jestjs


    【解决方案1】:

    问题是所有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 模拟模块,然后在导入所有内容后设置模拟的真正实现。

    【讨论】:

    • 这似乎是个好办法!我试过这个,但当Vocabulary 尝试调用vocabularyService.vocabulary.map 时会导致错误,因为vocabularyService.vocabulary 未定义。我注销了vocabularyService,它是一个开玩笑的模拟功能。也许这就是问题所在?
    • 啊抱歉,我的代码中有一个错误,mockImplementation 需要返回一个函数而不仅仅是对象。
    • 不适合我 :( 请参阅 stackoverflow.com/questions/45771844/….
    • 当我这样做时,我得到myService.mockImplementation is not a function
    • 这根本不起作用。将jest.mock 移动到beforeAll 会产生相同的错误,使用mockImplementation 会产生TypeError: mockedModule.mockedFunction is not a function
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      您需要将您的模拟组件存储在一个名称以“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
      【解决方案4】:

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

      在我尽我所能尝试之后。我决定清理项目并让我的所有测试重新开始工作。

      # react-native-clean-project
      

      但是在运行 react-native-clean-project 时要注意,它会清除所有的 ios 和 android 文件夹,包括原生代码,所以在提示时回答 N。就我而言,我刚刚选择了擦除 node_modules 文件夹。

      【讨论】:

        【解决方案5】:
        jest.mock("../../../src/services/vocabularyService", () => {
          // eslint-disable-next-line global-require
          const VocabularyEntry = require("../../../src/model/VocabularyEntry");
        
          return {
            vocabulary: [new VocabularyEntry("a", "a1")]
          };
        });
        

        我认为它也应该适用于动态导入,而不是 require,但没能成功。

        【讨论】:

          猜你喜欢
          • 2019-12-28
          • 2019-07-08
          • 2021-12-04
          • 2022-11-17
          • 2021-07-06
          • 2018-12-04
          • 2020-12-16
          • 2019-06-18
          • 1970-01-01
          相关资源
          最近更新 更多