我正在尝试模拟对服务的调用,但我遇到了以下消息:
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,因为它没有声明(因为笑话将模拟定义移动到文件顶部)。
任何人都可以解释一下我该如何解决这个问题吗?我看到了需要模拟调用中的引用的解决方案,但我不明白如何使用类文件来做到这一点。
您需要将模拟组件存储在名称前缀为“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
});
问题是所有
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")]
}))
这将首先使用一个简单的间谍来模拟模块,在导入所有内容后,它将设置模拟的真正实现。
如果您在升级到较新的 Jest 时遇到类似的错误(我的例子是 19 到 21),您可以尝试将
jest.mock
更改为 jest.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
,但没能成功。
在使用
jest.fn()
模拟的模块中实现 jest.mock
时,请确保执行以下两个步骤:
mock
开头。coolFunction: mockCoolFunction
的内容,请务必将其更改为 coolFunction: (...args) => mockCoolFunction(...args)
。这样,只要您没有运行该函数,Jest 就不需要知道 mockCoolFunction
是什么。欲了解更多信息,我推荐这篇博文。
对于我从事的一个项目,我尝试了此处列出的所有解决方案,但没有一个有效。
我是这样嘲笑模块的:
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(),
})
});
我怀疑这与项目特定的笑话或编译器配置有关。
就我而言,这个问题是在我使用react-native-git-upgrade将react-native项目升级到v0.61后开始的。
在我尝试了所有我能做的事情之后。我决定清理项目并让所有测试恢复工作。
# react-native-clean-project
但是在运行react-native-clean-project时要小心,它会清除所有ios和android文件夹,包括本机代码,所以在出现提示时只需回答N即可。就我而言,我只是选择擦除 node_modules 文件夹。