我正在编写一个React库MyLibrary
,并将其与Rollup.js2.58.3
绑定。我使用jest
进行单元测试。
问题的快速摘要
我无法使用jest
模拟库中的模块。这是由于"汇总"的方式;编译";我的代码。
Rollup使用其代码分割功能来创建许多块。在一个示例中,rollup将Alpha.js
拆分为两个块:Alpha.js
和Alpha-xxxxxx.js
。原始文件中的一些功能被提取到该"文件"中;中间体";块(Alpha-xxxxxx.js
(。
在我的单元测试中,当模拟移动到中间文件Alpha-xxxxxx.js
中的任何方法时,jest
似乎实际上是从这个";中间体";模块而不是顶级模块。
这会导致测试失败。
例如jest.mock('MyLibrary/dist/Alpha')
不工作,因为模块实际上是从Alpha-xxxxxx.js
而不是Alpha.js
加载的
举例详细说明
我在MyLibrary
中有两个模块Alpha.js
和Beta.js
。
MyLibrary/Alpha.js
export const Aaa = () => {
...
}
...
MyLibrary/Beta.js
import { Aaa } from './Alpha';
...
export const Bbb = () => {
...
}
...
汇总生成的编译输出
捆绑后,rollup.js将Alpha.js
拆分为两个块Alpha.js
和Alpha-xxxxxx.js
。由于代码分割,Beta.js
的编译版本现在看起来像这样:
MyLibrary/dist/Beta.js
import { Aaa } from './Alpha-xxxxxx';
...
export const Bbb = () => {
...
}
...
这个从MyLibrary
编译的模块被导入到MyApp
中,应用程序似乎正确导入了它,并且运行良好。
MyApp/index.js
import { Bbb } from 'MyLibrary/dist/Beta'
...
问题
下面的jest
测试失败,因为它没有正确模拟Aaa
导出。
MyApp/index.test.js
import { Bbb } from 'MyLibrary/dist/Beta';
jest.mock('MyLibrary/dist/Alpha', () => ({
Aaa: jest.fn(),
}));
然而,如果我模拟由rollup生成的中间块,它是有效的。
import { stuff } from 'MyLibrary/dist/Beta';
jest.mock('MyLibrary/dist/Alpha-xxxxxx', () => ({
Aaa: jest.fn(),
}));
如何在我的玩笑测试中正确地模拟库中的Alpha
?
听起来你需要模拟一个模块,它的文件名基础是确定的,但包括一个随每次构建而变化的哈希后缀。您还没有提供输出模块块文件名的完整模式,所以我将使用一个假设的示例:
假设您需要查找的区块模块以以下模式发出:
Alpha-b38ca4f6.js
这意味着它是文字Alpha-
,后面跟着长度为8的字母数字哈希,然后是扩展.js
你可以用这个正则表达式来表达:
^Alpha-[a-z0-9]{8}.js$
在将文件名传递给Jest之前,可以使用helper函数查找正确的文件名。函数应该接受一个目录和一个正则表达式来匹配文件模式。这里有一个例子:
MyLibrary/utils.mjs
:
import path from 'path';
import {promises as fs} from 'fs';
export async function findFileByPattern (dir, regex) {
const fileNames = (await fs.readdir(dir, {withFileTypes: true}))
.filter(entry => entry.isFile())
.map(entry => entry.name);
for (const fileName of fileNames) {
if (!regex.test(fileName)) continue;
const {name} = path.parse(fileName);
return path.join(dir, name);
}
throw new Error('File not found matching the provided pattern');
}
MyApp/index.test.js
:
import { Bbb } from 'MyLibrary/dist/Beta';
import { findFileByPattern } from './utils';
const chunkFileName = await findFileByPattern('MyLibrary/dist', /^Alpha-[a-z0-9]{8}.js$/);
jest.mock(chunkFileName, () => ({
Aaa: jest.fn(),
}));
如果需要,您甚至可以在构建后的步骤中使用该函数来定位区块文件名,并将该值作为JSON工件发出:然后在测试中导入JSON并使用文件名值。这将允许您跳过每次运行测试时枚举dist
目录的步骤。