首页 / 文章 / 在本地及持续集成环境中缩短 Jest 运行时间:Worker、缓存与作用域

在本地及持续集成环境中缩短 Jest 运行时间:Worker、缓存与作用域

提升 Jest 测试套件运行速度的实用检查清单:先进行性能测量,调整 Worker 数量,精简全局配置,重复使用缓存,启用 isolatedModules 模式,并仅运行受影响的测试。

956 词

一个运行缓慢的测试套件会悄然改变团队的工作方式:人们越来越少地运行测试,依赖持续集成系统来获取结果,而在最需要快速处理问题的生产环境紧急修复阶段却要等待最长时间。当人工智能编码助手能快速生成代码变更,而该测试套件又成为主要的安全保障时,这种成本还会进一步上升。Jest提供了众多配置选项和合理的默认值,因此直接使用通常就已经足够良好,但稍作调整就能显著缩短在笔记本电脑和持续集成系统中的运行时间。这些调整都不复杂,综合起来便能形成一份实用的检查清单。

调整前先测量

下面的每一项更改都会带来成本或权衡,因此应从数据入手。先禁用缓存(jest --no-cache)运行完整测试,获取初始基准值,然后再逐个应用更改并再次测量。

根据机器性能调整并行度

Jest 默认会在并行工作进程中运行测试文件,这通常很好,但并不总是最理想的方案。有两个参数可以控制这一点:

  • --runInBand 会在当前进程内以串行方式运行所有测试,不使用工作进程。对于那些测试需要共享昂贵资源的服务器端项目,或者是在核心数很少的 CI 运行环境中,这种方式可能会更快,因为创建工作进程带来的开销会超过其带来的收益。
  • --maxWorkers 用于设置 Jest 应该创建的工作进程数量。它可以接受一个数字或可用核心数的百分比;50% 是一个合理的起始值,能为机器的其他任务留下空间。

最佳数值取决于硬件配置,因此需要在本地以及 CI 运行环境中分别进行测试。CI 机器上报的核心数往往高于其实际负载下的可用核心数,过度分配工作进程只会让测试速度变慢,而不会变快。

保持全局配置简洁

在大型代码库中,全局配置文件非常方便:只需注册一次模拟对象、填充函数和测试工具,所有测试就能直接使用它们。但问题在于,每个测试文件都会为这些资源付费,包括那些根本不需要它们的文件。setupFilesAfterEnv中的大量导入、数据库测试数据或庞大的模拟对象注册表,都可能让原本毫秒级完成的单元测试变得缓慢。

将耗资源的配置移至真正需要它的测试附近:可以显式导入辅助函数,在相关文件中添加beforeAll钩子,或者为集成测试创建一个独立的Jest项目并为其配置专门的设置。

重用缓存

由于 Jest 会缓存转换后的文件及其他元数据,第二次运行通常会比第一次更快。在监视模式下这一点最为明显,但如果缓存能在不同任务之间保持不变,CI 流水线也能受益。将 cacheDirectory 指向一个稳定的路径,并利用 CI 系统的缓存功能将其持久化,以锁文件和 Jest 配置作为键值,这样每个流水线就不会从零开始运行。

在本地使用监视模式

对于本地开发,jest --watch 是 Jest 提供的最佳反馈机制。它只会重新运行与已修改文件相关的测试,而且其交互式提示功能允许你按文件名或测试名称模式进行筛选。该模式并不适用于 CI:流水线需要一次性运行并返回状态码,因此应将监视模式留在开发人员的机器上。

为 TypeScript 启用 isolatedModules

当 TypeScript 测试通过 ts-jest 执行时,对每个文件进行完整类型检查会带来明显的性能开销。启用 isolatedModules 后,转换器会单独编译每个文件,而不需要参考程序其他部分的类型信息。有团队在 Angular 项目中报告称这样做能显著提升速度,而且只要使用 tsc --noEmit 或让编辑器继续对代码库进行类型检查,安全性几乎不会受到影响。该选项的具体位置取决于你所使用的 ts-jest 版本,因此请查阅其最新文档。

仅测试变更会影响的部分

对于仅涉及一个包的修改,没有必要运行整个测试套件。Jest本身可以通过--onlyChanged或--changedSince=<branch>选项缩小测试范围,这些选项会利用版本控制来查找相关的测试用例。在单仓库项目中,像Nx这样的构建系统还能进一步理解项目结构,仅对受当前修改影响的项目运行测试。

应在某个地方至少保留一次完整测试,比如在主分支或夜间构建中,以便发现依赖关系分析可能遗漏的问题。

考虑使用Vitest

Vitest与Jest API高度兼容,维护活跃,且能与包括Nuxt在内的常见JavaScript框架配合使用。对于已基于Vite构建的项目而言,它往往是更自然的选择,许多团队在开展新项目时也会首先考虑它。上述关于性能测试、工作进程限制、精简配置以及仅运行受影响的测试的建议,同样适用于Vitest。如果您考虑完全放弃第三方测试运行器,请参阅用Node原生测试运行器替代Jest。

当软件优化无效时

最终,没有任何配置调整能比得上更快的硬件。一旦测试套件本身运行良好,更换一台较新的笔记本电脑或更大型的自托管CI运行器,可能是最经济有效的改进方式。

核心要点

  • 建立一个未缓存的基础状态,每次只修改一项设置。
  • 根据每个环境的实际处理能力来设定 --maxWorkers 或 --runInBand 的值。
  • 将耗时的初始化操作从全局钩子中移出,放到真正需要它的测试文件中。
  • 在多次 CI 运行之间保持 Jest 缓存不变;仅在本地使用监视模式。
  • 让 isolatedModules 跳过针对单个文件的类型检查,而通过单独的 tsc 步骤确保类型信息的准确性。
  • 在特性分支上仅运行受影响的测试,在主分支上则运行全部测试套件。