使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误
Posted
技术标签:
【中文标题】使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误【英文标题】:Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error 【发布时间】: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 将模拟定义移动到文件顶部)。
谁能解释我如何解决这个问题?我看到了需要在模拟调用中引用的解决方案,但我不明白如何使用类文件来做到这一点。
【问题讨论】:
【参考方案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
需要返回一个函数而不仅仅是对象。
不适合我 :( 请参阅 ***.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
,但没能成功。
【讨论】:
以上是关于使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误的主要内容,如果未能解决你的问题,请参考以下文章
在 jest-react 中使用 MockedProvider 会导致错误“查询没有更多的模拟响应”