目录
[隐藏]
1. 升级至 jest 28
升级后 package.json
中 jest 相关的主要依赖及版本参考如下:
{ "devDependencies": { "@jest/types": "^28.1.1", "@types/jest": "^28.1.1", "jest": "^28.1.1", "jest-environment-jsdom": "^28.1.1", "jest-extended": "^2.0.0", "ts-jest": "^28.0.5", } }
相比 jest@27 的相关依赖,除了更新版本外,如需 jsdom 执行环境,需单独添加 jest-environment-jsdom
库依赖。
2. jest 28 报错问题及解决参考
2.1 SyntaxError: Unexpected token ‘export’
主要报错信息参考如下:
node_modules/uuid/dist/esm-browser/index.js:1 ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){export { default as v1 } from './v1.js'; SyntaxError: Unexpected token 'export' > 1 | import { v4 } from "uuid";
chalk、uuid 这一类适配了最新 esm 方案的库,基本都会报此类错误。主要原因是 jest 使用 jsdom 执行时使用的默认导出为 browser-esm 版本,而 jest 28 默认的 transform 过滤了 node_modules 目录下的内容。
当前的解决方法可有以下几种:
方法一: 在 moduleNameMapper
配置中指定对应包的 commonjs 版本的入口。示例:
/** @type {import('@jest/types').Config.InitialOptions } */ module.exports = { testEnvironment: 'jsdom', moduleNameMapper: { '^uuid$': require.resolve('uuid'), // 或直接指定入口 js 路径 // '^uuid$': '<rootDir>/node_modules/uuid/dist/index.js', } }
方法二: 修改 jest transform 过滤规则,允许针对这些异常的依赖执行 transform。示例:
module.exports = { testEnvironment: 'jsdom', transformIgnorePatterns: ['/node_modules/(?!(uuid|xxx)/)'], }
提示:方法二由于使用 browser 方案,uuid 库还会因为当前版本的 jsdom 不支持 crypto.getRandomValues
而报错。
方案三:mock 不重要的依赖库。如果不是重度使用、对测试逻辑无影响的依赖,可以直接 mock 它。如针对 uuid 的 mock:
jest.mock('uuid', () => ({ v1: () => '110ec58a-a0f2-4ac4-8393-c866d813b8d1', v4: () => '110ec58a-a0f2-4ac4-8393-c866d813b8d1', v5: () => '110ec58a-a0f2-4ac4-8393-c866d813b8d1', });
2.2 setTimeout: Matcher error: received value must be a mock or spy function
许多涉及 setTimeout 调用的用例都报错如下:
Received has value: [Function setTimeout] expect(received).toHaveBeenCalledTimes(expected) Matcher error: received value must be a mock or spy function Received has type: function Received has value: [Function setTimeout] 111 | jest.runAllTimers(); 112 | // 定时器执行的次数 > 113 | expect(setTimeout).toHaveBeenCalledTimes(1);
查看测试代码发现存在 ts 类型异常。原写法为:
jest.useFakeTimers('legacy');
修正入参为如下即可:
jest.useFakeTimers({ legacyFakeTimers: true });
3 相关参考
- https://jestjs.io/zh-Hans/docs/upgrading-to-jest28
- https://jestjs.io/zh-Hans/docs/webpack
- https://jestjs.io/docs/ecmascript-modules
- https://hub.fastgit.xyz/facebook/jest/issues/12770
- https://hub.fastgit.xyz/jsdom/jsdom/pull/3352