升级 jest 28 后的单元测试执行异常问题及解决办法记录

目录
[隐藏]

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
点赞 (0)

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code