如何在使用Rollup.js时正确模拟Jest测试中的函数



我正在编写一个React库MyLibrary,并将其与Rollup.js2.58.3绑定。我使用jest进行单元测试。

问题的快速摘要

我无法使用jest模拟库中的模块。这是由于"汇总"的方式;编译";我的代码。

Rollup使用其代码分割功能来创建许多块。在一个示例中,rollup将Alpha.js拆分为两个块:Alpha.jsAlpha-xxxxxx.js。原始文件中的一些功能被提取到该"文件"中;中间体";块(Alpha-xxxxxx.js(。

在我的单元测试中,当模拟移动到中间文件Alpha-xxxxxx.js中的任何方法时,jest似乎实际上是从这个";中间体";模块而不是顶级模块。

这会导致测试失败。

例如jest.mock('MyLibrary/dist/Alpha')不工作,因为模块实际上是从Alpha-xxxxxx.js而不是Alpha.js加载的

举例详细说明

我在MyLibrary中有两个模块Alpha.jsBeta.js

MyLibrary/Alpha.js

export const Aaa = () => {
...
}
...

MyLibrary/Beta.js

import { Aaa } from './Alpha';
...
export const Bbb = () => {
...
}
...

汇总生成的编译输出

捆绑后,rollup.js将Alpha.js拆分为两个块Alpha.jsAlpha-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目录的步骤。

最新更新