🌑

测试如何加速你的开发

Read in English

作为前端开发者,我们大多数人都不喜欢写测试,因为”它会拖慢开发进度”。我以前也不写测试,我能理解你在为一个”快速迭代”的前端项目编写测试时的痛苦。但我确实想告诉你,在大多数情况下,测试可以加速你的开发,而不是拖慢它——除非你写的是错误的测试。我有一篇文章 Some Common Mistakes in React Testing 展示了哪些做法会让你在开发过程中感到痛苦。而在这篇文章中,我想向你展示为什么正确的测试能加速你的开发。

什么是开发?

我们是开发者,每天都在开发软件。但说真的,什么是开发?我们在日常工作中实际上做的是什么?
在《修改代码的艺术》一书中,作者认为开发的本质是行为变更。开发过程有四种类型:

  1. 添加功能
  2. 修复 Bug
  3. 改进设计
  4. 优化资源使用

每种类型都需要不同种类的行为变更:development type如你所见,我们在添加新功能时希望在软件中增加新行为,在修复 Bug 时希望改变旧行为。但当我们进行优化或重构时,通常不希望行为发生变化。如果我们在软件行为中引入了意外的变化,我们称之为回归(regression)

软件腐化

我们大多数人都有这样的经历:在一个遗留项目(一个没有良好代码结构和测试的大型项目)中修复 Bug 或添加新功能,比从零开始做一个新项目更加痛苦和困难。这是因为随着我们不断添加新功能,软件会日益腐化。代码将变得非常复杂,最终极难维护。幸运的是,我们有 React / Vue 框架将网站模块化为页面,再将页面模块化为组件。所以通常不会让每个组件都变得非常复杂,但到了某个时间点还是会的。即便是代码质量最高的团队也无法阻止软件腐化,因为解耦并不意味着你完全不会引入耦合

维护越来越难

因此,当行为变得越来越复杂时,我们会发现在确保现有行为不变的前提下进行开发变得愈加困难。当我们在组件中添加行为时,state 或 DOM 结构都会发生变化。如何保证现有行为得到保留?如何保证新行为按我们预期执行?以及,如何保证新行为能与现有行为良好协作
测试能给你信心。
preserving behavior

测试的角色

测试在开发中可以扮演两种角色:一种是验证,另一种是防止回归。这两种测试可以在不同的开发生命周期中帮助你以信心开发并提速

验证性测试

当我们向程序中添加新行为时,我们想知道新行为是否符合预期,怎么做呢?大多数前端开发者会编写代码,然后去本地开发环境检查行为变化。这就叫做手动测试。那么这种测试有什么问题呢?为什么自动化测试比手动测试更有效?这里有一个例子:
你开发了一个小组件,它在一系列步骤之后才会出现。假设我们要开发一个消息框,它会在提交一个需要填写 20 个字段的表单之后显示。
如果你使用手动测试:

  • 你需要填写 20 个字段(点击、点击、输入……),就像你的用户一样,然后点击提交。
  • 你需要祈祷服务器(包括你的本地服务器和后端)在测试过程中不会崩溃
  • 你需要祈祷依赖项(在本例中是 Form 组件)能正确工作
  • 你需要祈祷整个流程能一次性正确运行。如果不能,猜怎么着?你需要调试与整个流程相关的组件,包括但不限于 Form 组件、API 和消息框。
  • 你的同事审查你的代码并要求你修改部分代码,你需要用周全的测试用例再次进行点击输入的循环。真的很痛苦!

如你所见,手动测试就是在不停祈祷(开个玩笑)。当依赖项越来越多,组件本身越来越复杂时,情况会变得更加糟糕。
那么自动化测试怎么样?
你只需编写几行测试代码来验证这个消息框是否会根据相应的响应状态码显示正确的消息。然后你可以写一个使用一些模拟(mock)字段数据的测试,来验证它能以期望的数据和格式提交给后端。
我可以向你保证,编写这些测试并运行它们(对于典型的前端测试,通常只需几秒钟)所花费的时间,远少于你在本地开发环境中不断点击、输入、提交的时间。而且,如果你的测试失败,你可以知道哪个部分出了问题,而不是调试整个系统。并且你的测试可以运行数千次,这是自动化测试的一个显著优势。
这一切的核心在于隔离。 测试帮助你隔离每个组件,这样每次修改代码时,你都能获得快速、精准的反馈,而只需多写几行代码。

回归测试

验证性测试用于验证我们的新行为,而回归测试则是为了确保我们在修改代码时旧行为不会改变
你刚加入一个新团队,你的老板并不太喜欢你,他给了你一些工单,要求你在一个”遗留”项目中修复 Bug 并添加新功能。当你第一次查看项目代码库时,你被那混乱的结构惊到了,而且它没有任何测试和文档!所以你不得不做大量研究(通过逐一运行项目功能)来检查这个项目能做什么,以及哪些部分出了问题。
几天后,你鼓起勇气修改代码,通过修改某些 state hook 修复了一个 Bug。但猜怎么着,你仍然很有可能引入新的 Bug,因为你不知道这个 state hook 的用途。
所以如果项目之前已经写好了测试,你需要做的就是查看测试用例,因为测试用例可以作为文档,你可以了解项目(组件)在特定情况下会做什么。而且如果你修改了代码,在运行完所有测试后,你可以有信心地说你没有破坏重要功能。此时测试就充当了回归测试来防止回归
如果你接手的项目有测试,那你很幸运。而对于没有任何测试的项目,你仍然可以自己添加,以覆盖你想要修改的代码周围的行为

其他优势

自动化测试还能给你带来一些其他好处,以下是其中一些:

  • 文档:测试可以作为项目的文档,因为它模拟了用户如何与项目交互以及项目如何响应。
  • 迫使你理解需求:如果你不知道需求是什么,你就无法编写测试,因为测试本质上是将人类语言翻译成脚本。而如果你使用 Testing Best Practice Tdd你需要根据需求先编写测试,这会更好地迫使你首先思考需求。
  • 迫使你以良好的结构解耦代码:如果你的代码是耦合的,所有的东西都写在一个单独的组件中,你就很难为它编写测试,因为你无法再隔离它们了。所以测试可以帮助你思考代码的结构和设计是否合理

— Aug 18, 2022