今天想要给项目的 webpack 配置做个存档,所以首选了 Snapshot Testing 对最终生成的配置进行记录。
此时遇到了一个问题:配置文件中会出现一些绝对路径字符串,例如:"entry": "/local-path/my-app/src/index" 。这里的 local-path 就是我本地电脑的项目路径,很明显,这个路径在不同的电脑上可能会不同,这样这个单元测试快照就起不到基准的作用了。
为了解决这个问题,我需要将这个环境有关的信息给固定下来。
我首先想到的解决方案是 mock 掉这个字符串产生的源头。
mock path 模块
我发现这样的字符串产生的源头是如下代码:
const path = require('path');
path.resolve(appDirectory, relativePath)
既然这样,那我将 path 模块 mock 一下不就可以了?说干就干。。。
jest.mock('path', () => ({
...jest.requireActual('path'),
resolve: () => '/app-directory', // mock resolve 方法,返回一个固定的字符串
}));
运行一下,结果发现报错了:
Cannot find module '/app-directory' from 'config/paths.js'
为啥会这样报错呢?
原因是我项目中的 webpack 配置文件并不是一个单一的文件,是多个文件最终合并成一份配置,mock 了 path 模块之后,文件之间的导入导出被破坏了,导致配置压根无法生成。
所以并不能使用 mock 路径依赖模块的方法来解决这个问题。
使用 expect.addSnapshotSerializer(serializer) API
既然导入的源头无法解决问题,那么能不能在输出的地方做替换呢?也就是在生成快照的时候将数据替换掉。
经过一通查找文档,最终还真让我找到了对应的 api: expect.addSnapshotSerializer(serializer)。
配置示例 snapshotSerializers` [array]
// my-serializer-module
module.exports = {
serialize(val, config, indentation, depth, refs, printer) {
return 'Pretty foo: ' + printer(val.foo);
},
test(val) {
return val && val.hasOwnProperty('foo');
},
};
然后我先直接 copy 到本地看看配置是否有用,接着就报错了,摔。。。
PrettyFormatPluginError: Cannot read property 'plugins' of undefinedTypeError: Cannot read property 'plugins' of undefined
12 | const customSerializer = {
13 | serialize(val, config, indentation, depth, refs, printer) {
> 14 | return 'Pretty foo: ' + printer(val.foo);
| ^
15 | },
既然官网示例离奇不顶用,那就继续查找资料吧,然后就发现这个 Creating a Custom Jest Snapshot Serialize。这篇博文的例子和我的场景很搭配,示例如下:
// 这是上面那篇博文的例子,如果是 react 组件的 id 都给替换成 'ABC123'
expect.addSnapshotSerializer({
test: val => typeof val === 'string',
print: val => {
console.log(`val="${val}"`);
const newVal = val.replace(/^[A-Z0-9]{10}$/g, 'ABC123');
console.log(`newVal="${newVal}"`);
return `"${newVal}"`;
},
});
那我就直接照着写一个:
// 将配置中的本地路径替换掉,防止换个电脑就测试通不过
const customSerializer = {
serialize(val, config, indentation, depth, refs, printer) {
return (val as string).replace(appDirectory, '/app-directory');
},
test(val) {
return typeof val === 'string' && (val as string)?.includes?.(appDirectory);
},
};
expect.addSnapshotSerializer(customSerializer as any);
最终效果:
"entry": Array [
/app-directory/config/polyfills.js,
/app-directory/node_modules/react-dev-utils/webpackHotDevClient.js,
/app-directory/src/index.tsx,
],
完美~~
结语
本文讲解了如何自定义一个 jest 的序列化方法,如果你有相同的需求可以参考实现。
谢谢大家的观看。