编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

七爪源码:使用 TypeScript 时如何提高 CI 环境中的 Jest 性能

wxchong 2024-09-02 03:57:37 开源技术 13 ℃ 0 评论

消除项目卡住的测试用例和内存不足错误的提示

每个开发人员或开发团队在构建新项目时都必须做出决定,在这种情况下,我们谈论的是 Node.js 项目。在谈论 JavaScript 开发时,您可以做出的最佳决定之一是使用 TypeScript,因为这将为您提供额外的工具来编写更清晰和可维护的代码。

另一个不错的决定是在你的项目中添加一个测试框架或库,Jest 是目前最常用的框架并不是什么秘密,并且是一个不错的选择,因为它具有强大的内置断言库。

我们要添加的最后但并非最不重要的事情是一个管道,以确保我们不会引入错误并且在每次合并或发布之前一切都运行良好。让我们使用 Jenkins 来完成这项任务,尽管它可以是任何其他选项。

至此,您已经遇到了这个故事的三个主要人物:TypeScript、Jest.js 和 Jenkins。让我们开始吧。


问题

一切都开始了,因为它被要求更新项目依赖关系,因为检测到一些漏洞,而且距离上次更新已经很长时间了。在此过程之后,我更新了以下依赖项(仅提及最相关的):

typescript     from 3.9.7  to 4.7.4
jest               from 26.4.2 to 28.1.1
ts-jest           from 26.3.0 to 28.0.5

然后,当我推送更改并让 Jenkins 运行测试时,在两个不同的时刻发现了两个不同的问题:

测试阶段在每次流水线执行时都卡在随机测试用例中,并且从未完成。

在针对第一个问题实施解决方法并将更改合并到另一个功能分支与一些额外的代码和测试之后,在阶段的日志中打印了以下错误“JavaScript heap out of memory error”。

除此之外,重要的是要说在依赖关系更新之前,单元测试的测试阶段需要 1.59 分钟,e2e 测试需要 3.26 分钟。 记住这些数字以备后用。


“卡住的测试用例”的解决方法

正如您在此 Stack Overflow 问题的答案中所看到的,当您查找有关如何解决此问题的信息时,您可以找到的第一个也是最常用的方法与 Jest 用于运行测试的线程数有关。 您可以使用两个不同的选项来更改默认行为:

  • — runInBand:这会连续运行所有测试并使用主进程而不是创建更多线程。
  • — maxWorkers:这允许您设置可用于运行测试的线程数。 这默认为您机器上可用的内核数减去主线程的内核数。

因此,我在 package.json 测试脚本中添加了选项 — runInBand,以避免 Jest 一次运行多个测试,这意味着该进程需要更少的内存。 但是,这也意味着需要更长的时间才能完成。 这解决了第一个问题,但我们必须处理 JavaScript heap out-of-memory 错误。


深入挖掘,寻找“内存不足错误”的根本原因

为了更好地理解问题,我做的第一件事是从测试执行中获取更多的分析信息。 我通过将测试 package.json 脚本更新为以下内容来实现这一点:

{
  "scripts": {
    "test": "node --expose-gc ./node_modules/.bin/jest --runInBand --logHeapUsage",
  }
}

这使我可以单独运行测试并获取有关在运行每个测试后分配了多少内存的信息。

此时,我意识到 Jest 消耗的内存比 Jenkins 容器中的可用内存要多,并且开始运行第一个测试需要很长时间。

我的第一个选择是使用 SWC 加速测试,与 Babel 相比,它有望将编译速度提高 20 倍。但是,我在设置它时遇到了几个问题,因为它似乎无法很好地处理循环依赖关系。

然后,在谷歌搜索另一个选项时,我最终找到了一个名为“隔离模块”的 ts-jest 配置选项。简而言之,它禁用了 TypeScript 类型检查,从而减少了内存消耗和执行时间。


最终解决方案

我猜你担心类型检查,因为我们一开始没有使用 TypeScript 来禁用它。不过不用担心,我们只需要在 CI 环境中禁用它即可。正如您在下面的脚本中看到的,我们需要通过发送一个环境变量来禁用 CI 脚本的类型检查。然后,您只需要确保在配置管道时使用的是 test:ci。

pipeline {
    agent { docker { image 'node:18.7-alpine' } }
    stages {
        stage('test') {
            steps {
                sh 'npm run test:ci'
            }
        }
    }
}
const isolatedModules = process.env.ISOLATED_MODULES === 'true';
module.exports = {
  // [...]
  globals: {
    'ts-jest': {
      isolatedModules
    }
  }
};
{
  "scripts": {
    "test": "jest",
    "test:ci": "ISOLATED_MODULES=true node --expose-gc ./node_modules/.bin/jest --runInBand"
  }
}

然后让我向您展示使用普通测试和 CI 测试的测试执行的两个图像,以便您可以看到差异:

如果我们查看前面图片中的三个步骤,并对每个步骤进行比较,我们可以看到以下内容:

  1. 执行的第一个测试:启动时间和消耗的内存为 3,065s,CI 脚本中相应减少了 170MB
  2. 最后执行的测试:CI 脚本中的最终堆大小减少了 167MB
  3. 执行总结:CI 脚本中的执行时间减少了 11,375 秒

最后,让我告诉你,改进后的测试在 CI 环境中运行速度提高了 50% 以上。

谢谢阅读! 我希望这篇文章对您有所帮助。 欢迎所有建设性的反馈。


关注七爪网,获取更多APP/小程序/网站源码资源!

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表