为什么 Angular 抛弃 Karma 去拥抱 Jest 了

良好的测试对于构建高度复杂且可扩展的应用程序至关重要,也是项目成功的关键。作为一个眼光卓越的框架,Angular 在设计之初就考虑了测试的能力,并向开发者提供了许多测试功能。

使用 Angular CLI 生成项目时会默认使用 Karma 作为 test runner,Jasmine 作为测试框架。但是现如今,由于 Jest 的简单性、速度以及快照测试和模拟等内置功能,不少人开始尝试使用 Jest 代替默认的 Angular 测试方案。

并且根据有关 Angular 满意度的开发人员调查结果显示,Angular 的单元测试满意度也很低。主要原因是单元测试速度慢、重量级,并且由于 Karma 利用了真正的浏览器,导致单元测试难以与 CI/CD 集成。

上图是有关 Angular 满意度的开发人员调查结果(按团队规模和功能领域细分)

Angular 团队这几年是很注重社区反馈的,所以在 Angular 16 中,事情迎来了转机,Karma 被标记为弃用,而 Jest 也已经在实验模式下引入。这意味着 Angular 团队正在从 Karma 转向 Jest。但是目前官方网站只是简单介绍了怎样替换为 Jest,一些细节并没有描述清楚。所以,本篇文章首先会详细介绍如何将 Angular 中默认的 Karma 替换为 Jest,之后会探讨一下为什么这样做。

什么是 Jest

Jest 是 Facebook 出品的一款优雅、简洁的 JavaScript 测试框架,广泛应用于 React 生态系统中。其一大特点是内置了常用的测试工具,比如自带断言、测试覆盖率工具,实现了开箱即用。

而作为一个面向前端的测试框架, Jest 可以利用其特有的快照测试功能,通过比对 UI 代码生成的快照文件,实现对 React 等常见框架的自动测试。

与 Karma 和 Jasmine 相比,Jest 具有更好的开发人员体验和更快的执行速度。

关于 Jest 更详细的介绍,大家可以参考官方网站。

设置 Jest

接下来我们从零开始,一步步将 Jest 集成到我们的 Angular 项目中。

首先创建一个新项目。

bash 复制代码
ng new angular-jest-demo

删除 Karma 和 Jasmine 相关配置

作为一名有洁癖的程序员,首先要做的就是清除掉 Karma + Jasmine 相关的代码。

先卸载相关的 packages:

bash 复制代码
npm uninstall karma karma-chrome-launcher karma-coverage karma-jasmine karma-jasmine-html-reporter @types/jasmine jasmine-core

接下来从 angular.json 中删除当前的测试配置部分:

json 复制代码
"test": {
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
    "polyfills": [
      "zone.js",
      "zone.js/testing"
    ],
    "tsConfig": "tsconfig.spec.json",
    "inlineStyleLanguage": "scss",
    "assets": [
      "src/favicon.ico",
      "src/assets"
    ],
    "styles": [
      "src/styles.scss"
    ],
    "scripts": []
  }
}

添加 Jest 相关配置

1. 安装 jest 相关的 packages:

bash 复制代码
npm i - save-dev jest @types/jest jest-environment-jsdom

2. 在 angular.json 中把下面的 test 对象添加到刚刚删除掉的 test 对象的地方

json 复制代码
"test": {
  "builder": "@angular-devkit/build-angular:jest",
  "options": {
    "tsConfig": "tsconfig.spec.json",
    "polyfills": ["zone.js", "zone.js/testing"]
  }
}

3. 把 tsconfig.spec.json 中的 jasmine 替换为 jest

OK,现在我们运行 npm run test,就会发现测试已经正常运行了。

通过图中黄色字体可以看到,Angular 已经用 Jest 在运行测试了,并且也提示了我们目前还处于实验阶段。

深入探讨

介绍了如何使用 Jest,接下来我们看看为什么用 Jest。在 Angular 中使用 Karma 出了什么问题?替换为 Jest 有哪些好处?

Jest or Jasmine

我们先来看看 Angular 16 之前的情况。

文章开始也说过了,使用 Angular CLI 创建项目时,单元测试是开箱即用的,Angular 选择了 Jasmine。而 Jasmine 是在 Node.js 环境中运行的,而 Angular 作为一个前端框架,需要用到 DOM 和 Web API,于是 Angular 又引入了 Karma。所以就变成了 Karma 充当 Jasmine 的载体,它将测试嵌入到浏览器中运行。

在浏览器中运行测试的优点是可以访问 Web API 以及浏览器提供的所有其他内容,缺点是增加了一些开销。这就是 Angular 16 之前,Angular 官方提供的唯一测试方案。

而 Jest 提供了另一种选择,Jest 其实是在 Jasmine 之上构建的。Jest 不止改进了性能,也提供了更多的特性。

Jest 对于需要访问 Web API 的场景,走了另一条路: jsdom。

jsdom 是一个提供 DOM 和部分 Web API 的模拟器,允许我们和 DOM 交互。当然提供的功能无法和真正的浏览器相比,但是在大多数情况下,是可以满足测试需求的。

并且 Jest 还有一些强大的特性,比如 watch mode。watch mode 可以只运行受到当前更改影响的测试,极大提高了性能。

Jest 的各种特性使得它成为了 JavaScript 领域最主流的测试框架。

Jest 的缺点:ES Modules

然而,在 Angular 中使用 Jest 也不是没有缺点的,最大的一点就是对 ESM 的支持。

根据 Jest 官网显示,截止到 2023 年底,Jest 对 ESM 的支持仍处于实验性阶段。而 Angular 是全力推广 ESM 的。这导致了 Jest 与 Angular 的集成度与 Jasmine 不在一个水平上,Jest 需要自己进行编译。这个实际上是借助了社区的一个开源项目 jest-preset-angular 完成的。

事实上,在运行了 npm run test 之后,在 dist 目录下会生成一个 test-out 文件夹,Jest 会把测试文件、bundle 文件,都编译成 .mjs 文件。这也就导致了会出现 Jest 反而比 Jasmine 慢的情况。

并且 jest-preset-angular 也不能解决有关 Jest 构建过程中的所有问题。下面这张图很好的总结了在 Angular 16 之前,测试的解决方案:

Angular 测试的未来

终于在 Angular 16 的时候,Angular 团队介入了,决定正式支持 Jest。

根据 Angular 团队的说法,官方的 Jest 集成不会走 jest-preset-angular 的道路。Jest 将不再负责构建,Angular 会负责这部分。这意味着 Jest 会获得一堆 .mjs 文件,不需要遍历 node_modules、转译等操作。Jest 可以立即运行测试。

并且 Angular 在新的版本中开始支持 esbuild 了,整个测试的构建过程也使用了 esbuild,宣称比 webpack 构建快 50-100 倍。

需要注意的是,Angular 团队只把 Karma 标记成了 deprecated,并没有放弃 Jasmine 的打算。

在未来的版本中,Angular 将使用 ModernWeb 的 web-test-runner 取代 Karma。ModernWeb 是一个将测试嵌入到浏览器中的现代社区项目,感兴趣的同学可以了解一下。

所以,在未来,Angular 会提供 Jasmine/WebTestRunner (ModernWeb) 和 Jest 的组合。

总结

截止到 2023 年底,Jest 仍然处于实验性阶段,在生产中使用可能会遇到问题。具体使用哪种方案,可以根据项目实际情况决定。如果是新项目,个人会选择 Jest 作为方案~

参考资料

  1. Angular unit testing with Jest 2023
  2. 前端测试框架 Jest
  3. 改进 Angular + Jest 项目中组件测试的调试
  4. Moving Angular CLI to Jest and Web Test Runner
  5. Angular Testing in 2023: Past, Present, and Future
相关推荐
玲小珑1 分钟前
Next.js 教程系列(十五)复杂表单处理与数据校验
前端·next.js
Tina_晴6 分钟前
【基础篇下】Promise下的八种常见方法和终止Promise链条
前端·javascript·面试
Point6 分钟前
[ahooks] useMount useUnmount useUnmountedRef源码阅读
前端·javascript
蔓越莓7 分钟前
[bug] 中文输入法按回车键是先触发键盘事件还是先完成输入
前端·面试
国家不保护废物7 分钟前
浏览器多进程架构与EventLoop:从底层机制到代码执行的深度解析
前端·javascript·面试
mrsk7 分钟前
React useState完全指南:让组件拥有"记忆"的魔法
前端·react.js·面试
Hilaku7 分钟前
SSR, SSG, ISR, DPR:一篇文章讲清楚这些眼花缭乱的前端渲染模式
前端·javascript·架构
原则猫9 分钟前
jscodeshift 工程运用
前端·node.js
摆烂为不摆烂10 分钟前
💡前端入门:二进制在前端如何应用?从基础到实战全解析!
前端
LeeAt10 分钟前
前端异步请求的两种方式:XMLHttpRequest 与 Fetch
前端·javascript·https