是时候弃用 Enzyme.js 了。
当然这只是一种观点,并非事实,但我认为整个 React 生态系统和社区踏出这一步后都会变得更好。
2019 年,我在 AWS CloudWatch UI 团队工作时,是引入 React Testing Library 的幕后推手。那时我经常热情地倡导大家用它来取代 Enzyme。让人们提起兴趣去学习(又一个!)做同样事情的新 JavaScript 库当然绝非易事。但当我离开亚马逊时,我觉得这一运动是很成功的,并且 RTL 在我团队的项目中越来越受到关注。
全世界使用 React 的公司还有很多,他们也会因为放弃 Enzyme 而受益匪浅!在这篇文章里,我们就来诚实地讨论一下 React 世界在过去 5 年中发生了怎样的变化,以及为什么 Enzyme 应该被弃用。
本文最初发布于 Piotr Staniow 博客,经原作者授权由 InfoQ 中文站翻译并分享。
弃用在软件开发中意味着什么?
在编程语境中,弃用(deprecation)意味着向其他开发人员传达这样一种观点,那就是某个软件不再是首选解决方案,并且它已被更新的代码所取代。
这并不是说你就应该立即停止手头的所有工作,赶快去重写代码来用上更新的软件,也不代表那些已弃用的软件肯定不会再得到维护了。
相比之下,弃用表达的是我们希望逐渐有机地过渡到更新的解决方案上这一意图,因为我们认为新方案可以带来更好的用户或开发体验。
如今,市面上有两个用于测试 React 组件的库在相互竞争:Enzyme和React Testing Library,我想说服你的是不要在你的新代码中继续使用 Enzyme 了。
React 的一些历史
我们来快速回顾一下过去 5 年 React 世界中发生的事情。
2016 年,AirBnB 首次发布了 Enzyme——这是他们自己的 React 测试库。当时,AirBnB 是 React 生态系统的领导者之一,为众多开源项目做出了重大贡献。对于许多人来说,他们做的各种工具已成为行业的事实标准,当然也惠及了无数开发人员。他们还通过流行的 ESLint AirBnB 预设影响了开发人员的代码风格,重新定义了编写现代 JavaScript 代码的意义。
如果没有 Enzyme,我很难想象那个时候的测试会是怎么编写的。即使有了 Enzyme,那时候的测试也比现在写起来困难许多。我仍然清楚地记得在我的项目中安装和配置 Mocha、Chai、Sinon 和 JSDOM 的经历,它们加起来才能和今天的一个库——Jest——提供差不多的工具链,问题后者还是开箱即用的,且添加了许多额外特性。
世界一直在前进。2018 年 4 月(哇,已经 3 年了?!),Kent C.Dodds 在他的博客上宣布了 React Testing Library 的发布,这彻底改变了在 React 中编写单元测试的方法。
2019 年 2 月,React 团队发布了期待已久的新版本 React(16.8),该版本大幅改变了 API 并引入了 React Hooks。实际上,这完全改变了现代 React 代码的编写方式,生态系统中无数的库都被重写或修改以支持 Hooks。
我们都改变了自己对基于函数的组件的看法,以前我们认为它们只是无状态和纯表示形式的,现在则把它们看作是基于类组件的完全成熟的替代品。许多 React 教程在那一刻也就此过时了。
2020 年 2 月,AirBnB 宣布他们将 Enzyme 的所有权转移到了一个外部 GitHub 组织。虽然他们承诺继续支持 Enzyme,但他们也提到React Testing Library 在他们的项目中越来越受欢迎:
虽然 React Testing Library 等替代选项在 Airbnb 中获得了更多关注,但在可预见的未来,我们仍将继续使用 Enzyme 并为之做出贡献。
快进到 2021 年,目前只有一名开发人员在维护 Enzyme:
澄清一下,我是仅存的 enzyme 开发者。——Jordan Harband
背后要做的事情还有很多——React 团队已经承诺完全重写 React 文档,让它得到应得的更新。新文档还将反映现代 React 代码今天的样貌,这是一个带有 hooks 的函数式组件统治的世界。
类组件在未来几年内还会有一席之地——例如,Facebook 已经有数以万计的类组件处于生产环境。但我们确实建议使用函数式组件和 Hooks 来构建新的应用,所以我们要把这些文档放在最显眼的地方。使用类组件的人们还是能使用它们的文档,并且类组件本身可能有一天也会被分拆到他们自己的包中——但如果确实发生了这种事情,我们将提供迁移脚本来自动化这种迁移过程:)
——Rachel Nabor 的评论
为什么 Enzyme 应该被弃用?
原因很多,大体上可以概括为几个要点:
它长期以来一直落后于 React 的前进步伐,因此在阻碍人们过渡到更新的 React 版本
它依赖于 React 的内部实现,React 团队不鼓励使用它
它目前只由一个人维护——对于这么多使用它的公司来说,只依靠一个人来维护他们的一个关键软件是有风险的
它助长了一些糟糕的测试实践,并且 Enzyme 中的测试无法代表客户体验
市面上有了一个更好的解决方案,这个行业已经在前进了
不支持新的 React 特性
根据我的经验,在过去的 3 年里,几乎每次都是 Enzyme 不支持 React 的最新特性,而且几个月都跟不上趟。
如果你正在编写新代码,我认为你最好使用较新版本的库,因为它们通常会获得更好的支持并符合行业标准。
看看 Enzyme 的问题跟踪器你就会发现,它仍然不能完全支持大约 3 年前发布的 React 16。
对 React 17 的支持也是近一年来的一个开放问题,你只能在 6 个试图为 Enzyme 提供 React 17 支持的非官方适配器中做出选择,每个适配器都解决了自己的一部分问题,也有自己的一些麻烦。
提醒一下,React 18 Alpha 最近发布了——虽然一切都可能发生变化,但它很可能会在几个月后就正式发布。
Enzyme 的一个弱点是,Enzyme 中实际上有一些 API 只适用于基于类的组件,并且它们没有基于函数的等效组件。
现实情况是,如果你今天正在编写新代码,你可能不希望使用 Enzyme 来测试它。如果你正在编写现代 React 代码,用 Enzyme 的话很有可能你迟早会遇到问题(比如这个或这个问题)。到头来,你要么做一些修补来解决问题,要么更改测试场景以匹配这个库的功能限制,要么留下未经测试的代码段。
React 核心团队不鼓励这样做
这个论点不一定会引起你的共鸣,但对我来说,React 背后的专家不鼓励使用 Enzyme,并建议改用 React Testing Library,这一点是很重要的。
毕竟,我们都相信他们对 React 实现的计划,以及他们塑造 React API 的方式。所以当他们推荐使用 React Testing Library 时,他们应该是有着充分理由的。
有许多迹象表明整个行业已经在向前发展。似乎 Facebook——React 的诞生地——已经冻结了他们的 Enzyme 测试并禁止在任何新测试中使用它:
虽然与这个问题无关,但我认为值得注意的是,如果你可以使用像 React Testing Library 这样不依赖于 React 内部组件的项目,往往会是一个好主意。在 FB,我们冻结了 Enzyme 测试,只保留在因为这个原因不会升级的旧版本 React 上,我们禁止在任何新测试中使用它。我不想偏题到其他话题上,但对于那些因此感到麻烦的人们,我想表示一些鼓励,告诉大家换一种方法是一项值得去做的投资。——Dan Abramov 的评论
再看看今天官方 React 文档中的内容,他们实际上建议你使用 React Testing Library。当然,在某些情况下你是无法使用 RTL 的,我也清楚。培训开发人员学习新技术肯定会增加成本,这会产生一些摩擦。你可能也不会花费数百个开发小时来重写你的那些久经实战考验的测试,毕竟这可能会给它们带来错误。
这样也很好!并不需要急着改变,毕竟这不是什么安全问题。
不过,我认为这是一条重要的信息——React 背后的专家和社区已经评估了各种选项并做出了选择。如果你不想深入研究为什么 RTL 比 Enzyme 更好,那么你应该先去看看官方文档来了解相关知识。
它只由一个人维护
时至今日,Enzyme 只剩下一个人维护了——他就是Jordan Harband。他是一位多产的开源贡献者,是 TC39 委员会的成员(他们定义 JavaScript),并且是一位真正的英雄,他独自维护着这款为全球数百万个测试套件提供支持的工具。
但现实是,他大概需要把时间分配在许多项目上,而且他和我们所有人一样有自己的个人生活。指望一个人可以很好地维护 Enzyme 这样对开发人员的工作如此核心的东西,让他自己处理我们遇到的所有小麻烦和边缘情况,显然是不合情理的。
事实上,任何公司将如此多的责任放在一个人身上都是危险的。即使在今天,我们也看到了这种情况的副作用:有些测试没有编写,有些案例需要花几个小时来调试错误,或者我们没有使用有些特性来简化代码库,仅仅因为 Enzyme 没能跟上时代。另一个角度来看,我们可能不应该对任何人施加如此大的责任和压力。
问题本质上来说当然是更复杂的,我们应该问一下为什么在使用 Enzyme 的数千家公司中,真正为它的开源代码做出贡献的公司如此之少。要知道这些开源代码是为他们带来收入的啊。我认为现实情况不会很快改变,我们必须应对目前这种远非理想的处境。今天,Enzyme 只有一位开发人员在维护,而且就算它有了更多维护者,解决 Enzyme 的所有问题也需要几个月的时间。
某种程度上更容易误用
在我见过的所有用 Enzyme 和 React Testing Library 编写的单元测试中,我认为误用 Enzyme 比 RTL 更容易。
它们代表了两种不同的方法,其中 Enzyme 为组件提供了一些包装器,而 RTL 专注于以“客户看到它们的方式”(DOM 表示)来渲染组件。
可能是因为这些年我在 Enzyme 上看到的测试比较多,不管怎样我看到了太多无意义测试的例子。比如一个测试使用一个 spy 函数作为 prop 浅渲染一个组件,接下来使用 Enzyme 的.props()方法提取这个 prop,直接调用它并断言它要被调用。
这有意义吗?我是觉得它毫无意义,但类似的事情还是发生了,我认为 Enzyme 让大家太容易通过这种测试创建空行覆盖率了。
我更经常看到的是组件 props 的快照,它完全没有说明测试用例的意图,并且将测试与内部实现细节紧密耦合起来。
虽然使用 React Testing Library 也肯定有很多方法可以走偏,但根据我的经验,RTL 是一款出色的软件,可以让人们编写更有意义的测试。
这个行业已经在前进了
React Testing Library 在“其他人将如何使用这个库?”和“它鼓励我采用哪些实践?”方面考虑得非常周到。我更喜欢这种方法,而不是 Enzyme 的那种——虽然后者非常强大,但同时也留下了太多实现同一目标的不同方法。
一个例子是 RTL 专注于测试客户体验——毕竟,这才是真正重要的。我们的客户不会看到我们的组件有什么 props,或者它们是数组还是对象,客户只关心它是否正常工作——并且会帮助你在未来交付价值。
React Testing Library API 也是如此,它提供了查询函数,让你可以使用可访问性特性获取 DOM 元素。这是引入那些特性的一个很好的桥梁,这些特性可以支持你的站点上大约 10%可能需要它们的用户。
用 RTL 编写这样的测试也比那些基于浏览器的测试更便宜,因为它们很少出现不稳定状况,而且更容易维护。同时,你正在操作的是非常强大的原生 DOM 元素(好吧,是这些元素的 jsdom 实现)。它不需要你学习任何额外的 API,只用那些我们都知道的原生浏览器 API 就行。
如果你正在思考该如何拆分你的测试,我鼓励你在 React Testing Library 中编写尽可能多的测试。如果某些东西不能在 RTL 中测试(比如通过拖放在 SVG 中绘制一个矩形!),那就回退到 Puppeteer 或 Cypress——例如当你需要计算实际样式,或制作一些端到端测试的时候。
如果 RTL 不支持某些内容,你还可以使用整个 DOM。不幸的是,如果底层元素来自函数式组件,Enzyme 是不会让你获取的,而是会引发错误。
不管怎样,整个行业已经在向前发展了。Enzyme 过去一年的每周下载量稳定在 2.1-2.5M 左右。与此同时,React Testing Library 的每周下载量从 180 万次增加到超过 400 万次。
Enzyme 每周下载
React Testing Library 每周下载
在 2020 年的 JS 状态调查中,开发人员发表了他们对 React Testing Library 的看法——在那些听说过它的人群中:
58%的开发者已经使用并会使用它,
另有 30%的人听说过并愿意使用它。
最后,GitHub Insights 工具提供了有关两者使用情况的有趣统计数据。在撰写本文时,在 GitHub 上托管的开源存储库中:
354,059 个存储库依赖 Enzyme
2,440,909 个存储库依赖 React Testing Library
接下来呢?
现在看来,整个业界似乎已经转向 React Testing Library 了,这是有很多很好的理由的。
很难说 Enzyme 在不久的将来是不是有哪一天就会被弃用了,但这似乎是一个不可避免的结局,可能已经开始倒计时了。虽然今天还有许多人在使用 Enzyme,但我预计这一数字很快就会急剧减少。
今天就弃用这个库将帮助许多公司避免引入不必要的技术债务,并向整个社区发出明确的信息,那就是 React Testing Library 才是未来。
这不是说“立即停止使用它”,也不是说“立即重写所有测试”。但是,你可能不应该使用 Enzyme 测试来覆盖新编写的代码。这两个库可以安全地共存,你可以简单地禁止使用 Enzyme 来测试新代码,让开发人员有机地逐步过渡过去。
React 的未来在于基于函数的组件、React Hooks、异步渲染——而这些特性今天最好搭配 React Testing Library 使用。纵观 Enzyme 过去三年的发展历程,它似乎不太可能赶上所有这些特性,同时还能解决其他那么多问题。
现在是时候弃用 Enzyme 了。
评论