为什么需要自动化测试
尽管 PHP 的标准单元测试框架 PHPUnit 早在 2006 年就已经存在,但如今自动测试仍未在所有 PHP 项目中使用。这就浪费了很多潜力,因为自动测试有很多好处,比如以下几点:
-
速度和可靠性:设想一下,你需要反复执行相同的测试步骤。很快,你就会犯错误,或者干脆跳过测试。然而,自动测试能以更快、更可靠的方式为你完成这些枯燥的工作,而且它们不会抱怨。
-
文档:通过自动测试,你可以通过断言间接地记录代码的功能,这些断言解释了代码应该做什么。与 wiki 中的注释或文章相比,当某些内容发生重大变化时,失败的测试会立即通知你。我们将在第 13 章 "创建有效的文档" 中再次讨论这个话题,届时我们将讨论创建有效的文档。
-
上机:一个对代码覆盖良好的测试套件能帮助新开发人员更快地在项目中取得成效。测试不仅可以作为额外的文档,还能让开发人员放心地进行修改或添加功能。他们可以验证他们的更改在部署到任何暂存或生产环境之前不会破坏任何东西。
-
持续集成/持续部署(CI/CD):无论是 CI 还是 CD,如果你的测试是自动化的,你就可以通过构建管道相信你合并的代码不会被破坏,这使你能够更快地将代码推送到生产环境,从而更频繁地进行生产。下一章,我们将深入探讨这一主题。
-
更好的代码:你不必严格遵循臭名昭著的测试驱动开发(TDD)方法,就能从开发中的测试中获益。编写可单元测试的代码甚至可以改进代码。要想在隔离状态下测试代码(例如,在后台不运行真实数据库的情况下),就需要在编写代码时考虑到分离问题。如果使用依赖注入(DI)方式注入外部依赖关系,那么用测试对象替换外部依赖关系要比在类函数中实例化外部依赖关系容易得多。我们将在第 12 章 "团队工作" 中详细介绍 DI 模式。此外,复杂的长函数和短函数同样难以测试(例如,我们在第 8 章中讨论过的 NPath 复杂性),因此你很快就会开始编写更短的函数,以减少代码中的决策路径数量。
-
更容易重构:当你想根据第 7 章中介绍的静态代码分析器的结果重构项目时,自动测试是一个非常宝贵的工具。你可以应用它们的建议,甚至是自动代码修复,只需在运行测试后,就能知道这样做是否会带来任何副作用。由于重构是本书中最重要的用例,我们将在下一节详细讨论。
通过测试更轻松地重构
如果你在绿色环境中开始一个项目(即从头开始编写),你可以在开始编码后立即从代码质量工具中获得反馈。这是一个很大的帮助,但即使是最好的工具也无法阻止你做出错误的决定,写出你想在某些时候撤销的代码。
这种情况在每个人身上都会发生,完全不应该让你气馁。你每天都会学到新的东西,在你的个人技能不断发展的同时,你的代码也会不断发展。如果你看看自己一年前的代码,你可能会想马上重构它。
当然,不仅是你的技能,整个 PHP 生态系统也在不断改进。许多如今已成为标准的东西在过去根本不存在。新的软件包或语言特性不断推出,你希望在你的项目中使用它们,而不是永远坚持旧的技术。
因此,代码会随着时间的推移而改变,这是完全正常的,作为开发人员,我们应该拥抱变化;我们的代码没有一段是最终的。我们将改变现有代码称为重构。重构的有趣之处在于,代码被修改了,但软件在用户看来却没有变化。所有工作都发生在 "引擎盖 "之下。举例来说,如果你将项目的框架更新到了最新版本,而用户没有发现任何直接的变化,那么你的工作就做得很好。
重构是有好处的,否则我们就不会这样做。如果方法得当,重构可以提高性能、增强安全性,或者使应用程序在云中具有可扩展性。然而,重构往往带有不好的含义。尤其是管理者,他们往往认为重构意味着改变代码,只是因为工程师们想要追随网络开发世界中的另一种热潮,从而浪费了宝贵的工作时间。
老实说,这种情况当然也会发生。界限往往很难划分。例如,假设你的职责是维护一个使用单例模式(Singleton pattern)进行对象实例化的老旧但运行良好的 PHP 应用程序。如果你只是偶尔需要做一些小改动,那么实际上就没有必要重构它以使用 DI。但是,如果你需要实施持续的更改,例如为其添加新模块和测试,那么这样做可能是一个不错的选择。
通常情况下,你必须证明你的重构工作是合理的。这时候,倒不如说是对代码进行系统健康维护,这样会更有帮助。每个人都知道机器需要维护:零件需要更换,润滑油必须更新,等等。然而,出于某种原因,我们的软件应该永远运行下去。
有了重构的充分理由后,我们想了解测试如何在这方面帮助我们。为了做到这一点,让我们在下一节仔细研究一下不同的测试类型。