跳至主内容

Jest 28:轻装上阵,兼容性更佳 🫶

· 9 分钟阅读
Simen Bekkhus
Simen Bekkhus
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

Jest 28 终于来了!它带来了许多开发者期待已久的功能,包括支持在多台机器上分片(sharding)运行测试、package exports支持,以及自定义模拟计时器(fake timers)行为的能力。这些只是部分亮点功能,我们将在本文中详细介绍更多更新。

此外,正如我们在去年的Jest 27博客文章中所预告的,我们已从默认安装中移除了不再默认使用的包。这使得安装体积减少了约三分之一。

破坏性变更

破坏性变更列表较长(完整内容可查看更新日志),为方便迁移,我们还编写了迁移指南。希望这能让您的升级过程尽可能顺畅!

主要可能影响迁移的破坏性变更包括:停止支持 Node 10 和 15(但_不包括_ Node 12,它将在几天后结束支持期)以及部分配置选项的重命名。

请注意,被移除的两个模块(jest-environment-jsdomjest-jasmine2)仍以相同方式积极维护和测试,因此唯一的破坏性变更就是您需要显式安装它们。

该指南应该能让迁移变得轻松,但请注意:如果您直接使用了 Jest 的组成包(如 jest-workerpretty-format),而不仅仅是运行 jest 命令,那么您需要查看更新日志以了解所有破坏性变更。

新特性

现在让我们聊聊 Jest 28 更令人兴奋的新功能吧!这次更新带来了不少新特性,请系好安全带。

测试运行分片

Jest 现在新增了 --shard CLI 选项,由 Mario Nebl 贡献。它允许您在多台机器上分片运行测试,这是 Jest 历史最悠久的功能需求之一。

在 CI 环境中,Jest 自身的测试套件在 Ubuntu 上从约 10 分钟缩短到 3 分钟,在 Windows 上从 20 分钟缩短到 7 分钟。

package.json exports

Jest 27.3 已提供对 exports 的基础支持,但仅支持主入口点(.),且仅在 package.json 中不存在 main 字段时生效。在 Jest 28 中,我们终于提供了完整支持!

相关改进:在 Jest 27 中,我们仅提供 requireimport 条件。而在 Jest 28 中,jest-environment-node 现在会自动提供 nodenode-addons 条件,而 jest-environment-jsdom 则会提供 browser 条件。

这一直是 Jest 最突出的兼容性问题,希望此次更新能彻底解决。

模拟计时器

Jest 26 引入了"现代"模拟计时器的概念(底层使用 @sinonjs/fake-timers),Jest 27 将其设为默认。在 Jest 28 中,我们通过配置和运行时 API 开放了更多底层实现。特别感谢贡献此功能的 Tom Mrazauskas

这样您就无需模拟 process.nextTick,从而提升与伪造 Promise 的兼容性;或者启用 advanceTimers 功能来自动推进计时器。

详情请参阅 fakeTimers 配置文档

GitHub Actions 报告器

Jest 现已内置适用于 GitHub Actions 的报告器,该功能会通过注解在代码行内直接显示测试错误。

GitHub Actions 测试错误截图

您只需在 reporters 配置选项 中传入 github-actions 即可启用此报告器。

衷心感谢 Bernie Reiter 及其他贡献者长期以来的坚持,最终促成此功能落地。

内联 testEnvironmentOptions

现在您可以直接在文件中内联传递 testEnvironmentOptions,这类似于设置测试环境的方式。当您需要在单个文件中修改 URL 等配置时特别实用。

/**
* @jest-environment jsdom
* @jest-environment-options {"url": "https://jestjs.io/"}
*/

test('use jsdom and set the URL in this test file', () => {
expect(window.location.href).toBe('https://jestjs.io/');
});

完整 Node.js 全局对象支持

如果您在 Node v18 中使用新的 fetch 实现,可能已注意到此函数在 Jest 中不可用。过去我们必须手动将所有全局对象复制到测试环境,这曾是长期存在的问题。Jest 28 通过检查 Jest 自身运行的全局环境,并自动补全测试环境中缺失的全局对象,彻底解决了该问题。

ECMAScript 模块支持

自 Jest 27 发布以来,我们对原生 ESM 的支持未有重大变化。目前仍受限于 Node 的稳定化进程,期待该问题能早日取得进展!

不过我们仍在 Jest 28 中实现了若干新功能。

data: URL 支持

Tommaso Bossi 贡献了对 data URL 的支持,这意味着您现在可以直接内联定义 JavaScript 代码并执行,无需使用 eval

import.meta.jest

虽然您始终可以通过 import {jest} from '@jest/globals' 访问 jest,但我们收到反馈称这种方式不如 CJS 中(看似全局实则非全局的)jest 变量便捷。为此 Jest 28 提供了 import.meta.jest 以实现更便捷的访问。

其他改进

以上是我个人重点介绍的功能亮点,但实际上我们还有更多改进值得关注:

异步解析器

Ian VanSchooten 贡献了对异步解析器的支持,这将使 Vite 等工具能更好地与 Jest 集成。

异步设置文件

现在当您使用 setupFiles 时,可以导出一个 async function 来执行异步操作,Jest 将在加载任何测试前调用并等待该函数执行完成。

请注意此功能仅适用于 CJS 模块。对于 ESM 模块,我们建议改用顶层 await 方案。

使用 globalThis

Jest 内部一直使用 global 来指代全局环境。但由于这仅在 Node 环境中存在(浏览器环境对应 window),导致在其他环境中使用 Jest 模块时出现兼容性问题。

Jest 28 改用 globalThis 实现,该特性在所有环境中均可使用。

JSDOM 19

如前所述,虽然 Jest 默认安装包不再包含 jest-environment-jsdom,但该模块仍持续维护。作为更新的一部分,Jest 28 已将 jsdom@16 升级至 jsdom@19

TypeScript

如果您在测试或编写插件(如自定义运行器)时使用 TypeScript,Jest 28 对类型系统进行了全面增强。以下是 Jest 28 类型改进的非详尽列表:

expect

当使用 expect 自有类型(直接使用或通过 import {expect} from '@jest/globals' 导入)时,现在终于可以添加自定义匹配器了。具体实现方法请参考我们的示例

自定义插件

如果您编写自定义运行器、测试报告器、解析器等插件,我们现在导出了更多类型帮助您准确定义。这是一个持续改进的领域,如果您是 Jest 可插拔组件的作者且发现类型支持不足,请提交 issue 反馈!

jest-runner-tsd

jest-runner-tsd 是用于运行类型测试的自定义运行器。Jest 自身就使用它来测试类型系统,我们也希望其他开发者能从中受益!顾名思义,它基于 tsd 实现,底层实际使用的是其分支 tsd-lite


以上所有改进和修复均由 Tom Mrazauskas 贡献。衷心感谢 Tom!👏

最后,TypeScript 最低支持版本现已提升至 4.3。

jest-light-runner

本文最后要重点介绍的是由 Nicolò Ribaudo 开发的超酷新运行器 jest-light-runner。它在保留 Jest 标志性开发者体验的同时,通过精简 Node 抽象层大幅提升速度。Babel 测试套件迁移后速度提升近一倍。虽然存在使用限制,但该运行器能让测试小型 Node 模块的开发者更轻松地选择 Jest。感谢 Nicolò!

未来规划

虽然 Jest 28 距上个主版本发布近一年,但 Jest 29 将在数月内推出。当前计划的核心变更(除弃用 Node 版本外)是将 snapshotFormat 默认值设为 {escapeString: false, printBasicPrototype: false}。这将使快照更易读且更便于直接复制使用。

当然您可以选择覆盖该配置,如果不想等待,现在就可以启用这些选项!

致谢

Jest 28 汇集了 60 多位贡献者的智慧成果,其中超过三分之二是首次贡献者。衷心感谢所有新老贡献者!没有你们,这个项目绝不会如此出色!特别感谢 汤姆·姆拉佐斯卡斯冯宇,他们从代码编写、问题分类到调试优化全程参与,正是这些贡献铸就了今天的 Jest 28。感激不尽!🙏

感谢阅读,祝您测试愉快!🃏