PHP 自动化测试指南
为你的 PHP 代码编写自动化测试被业界公认为是一种“最佳实践”,它是构建高质量、健壮应用程序的基石。当你修改代码或添加新功能时,自动化测试是一个绝佳的工具,能确保你的应用不会“牵一发而动全身”地崩溃。这是任何成熟开发者都不应忽视的环节。
目前 PHP 生态中有许多不同类型的测试工具(或框架),它们采用了不同的理念和方法。但它们的核心目标都是一致的:避免繁琐的手动测试,减少对庞大 QA(质量保证)团队的依赖,仅仅是为了确认最近的代码修改没有破坏原有的功能。
1. 测试驱动开发 (Test Driven Development, TDD)
根据维基百科的定义:
测试驱动开发 (TDD) 是一种依赖于极短开发周期不断重复的软件开发流程:首先,开发者编写一个会失败的自动化测试用例,以此来定义一个期望的改进或新功能;接着,编写恰好能让该测试通过的业务代码;最后,将新代码重构到符合可接受的标准。因开发或“重新发现”该技术而闻名的 Kent Beck 在 2003 年指出,TDD 能够鼓励简单的设计并激发开发者的信心。
针对你的应用程序,你可以进行以下几种不同类型的测试:
2. 核心测试类型
2.1 单元测试 (Unit Testing)
单元测试是一种编程方法,用于确保函数、类和方法从你构建它们的那一刻起,直到整个开发周期结束,都能按预期工作。
通过检查各种函数和方法的输入值和输出值,你可以确保内部的逻辑运转正确。借助于依赖注入 (Dependency Injection),并构建 “模拟对象 (Mock)” 类和存根 (Stubs),你可以验证依赖项是否被正确使用,从而获得更高的测试覆盖率。
当你创建一个类或函数时,你应该为它必须具备的每一个行为编写一个单元测试。在最基础的层面上:
- 如果传入错误的参数,确保它能正确报错。
- 如果传入有效的参数,确保它能正确运行。
这能保证在开发后期的任意时刻,无论你怎么修改这个类或函数,旧有的功能依然能如预期般工作。如果不写单元测试,你唯一的替代方案就是在 test.php 里狂敲 var_dump()——这绝对不是构建应用程序(无论大小)的正规方式。
单元测试的另一个重要用途是参与开源贡献。如果你能写一个测试来展示某个被破坏的功能(即测试运行失败),然后修复代码,最后展示测试通过,那么你的代码补丁 (Patch) 被合并的可能性会大大增加。如果你自己维护着一个接受 Pull Request 的开源项目,你强烈建议把“必须包含测试”作为一项硬性要求。
PHPUnit 是编写 PHP 应用程序单元测试的事实标准框架,但社区中也有许多优秀的替代方案:
- atoum
- Kahlan
- Peridot
- Pest (近年来极其流行、语法极其优雅的测试框架)
- SimpleTest
2.2 集成测试 (Integration Testing)
根据维基百科的定义:
集成测试(有时也称为 Integration and Testing,简称“I&T”)是软件测试中的一个阶段,在这个阶段中,各个独立的软件模块被组合在一起并作为一个整体进行测试。它发生在单元测试之后、验证测试之前。集成测试以已经通过单元测试的模块作为输入,将它们聚合成更大的集合,然后对这些集合应用集成测试计划中定义的测试,最终输出准备好进行系统测试的集成系统。
由于集成测试和单元测试使用了许多相同的基本原则,因此很多用于单元测试的工具,同样可以直接用来编写集成测试。
2.3 功能测试 (Functional Testing)
功能测试有时也被称为验收测试 (Acceptance Testing)。功能测试是指使用自动化工具来真正地“使用”你的应用程序,而不是仅仅验证各个代码单元是否行为正确,或者模块之间是否能互相通信。
这些工具通常使用真实的数据,并在浏览器或环境中模拟应用程序的真实用户。
主流的功能测试工具:
- Codeception:一个全栈测试框架,内置了强大的验收测试工具。
- Cypress
- Mink
- Selenium
- Storyplayer:一个全栈测试框架,支持按需创建和销毁测试环境。
3. 行为驱动开发 (Behavior Driven Development, BDD)
行为驱动开发 (BDD) 主要分为两种截然不同的类型:SpecBDD 和 StoryBDD。
- SpecBDD 侧重于代码的技术行为。
- StoryBDD 侧重于业务或功能的用户行为与交互。 在 PHP 中,这两种类型的 BDD 都有对应的成熟框架。
StoryBDD:
在 StoryBDD 中,你编写的是人类可读的“故事 (Stories)”,用以描述应用程序的行为。然后,这些故事会被直接当作实际的测试用例来运行。PHP 应用程序中用于 StoryBDD 的框架是 Behat。它的灵感来源于 Ruby 的 Cucumber 项目,并实现了 Gherkin DSL (领域特定语言) 来描述功能行为。
SpecBDD:
在 SpecBDD 中,你编写的是描述实际代码应该如何表现的“规范 (Specifications)”。你不是在“测试”一个函数或方法,而是在“描述”这个函数或方法应该有什么样的行为。PHP 为此提供了 PHPSpec 框架。该框架的灵感来源于 Ruby 的 RSpec 项目。
BDD 相关链接:
- Behat:PHP 的 StoryBDD 框架,受 Ruby 的 Cucumber 项目启发。
- PHPSpec:PHP 的 SpecBDD 框架,受 Ruby 的 RSpec 项目启发。
- Codeception:一个融合了 BDD 原则的全栈综合测试框架。
4. 辅助测试工具与扩展库
除了针对不同层级和 BDD 的测试框架外,PHP 社区还有许多通用的框架和辅助库,无论你采用哪种测试方案,它们都能为你提供强大的助力。
工具列表与链接:
- Selenium:一款强大的浏览器自动化工具,它可以与 PHPUnit 完美集成。
- Mockery:一个极其流行的模拟对象 (Mock Object) 框架,可以轻松与 PHPUnit 或 PHPSpec 集成。
- Prophecy:一个具有强烈主见、同时又非常强大灵活的 PHP 对象模拟框架。它与 PHPSpec 深度集成,也可在 PHPUnit 中使用。
- php-mock:一个专门用来帮你模拟 PHP 原生内置函数(如 time(), rand() 等)的实用库。
- Infection:一个 PHP 版本的变异测试 (Mutation Testing) 实现工具,专门用来帮你测量测试用例的真实有效性。
- PHPUnit Polyfills:一个兼容库。当你的测试套件需要在多个不同版本的 PHPUnit 上运行时,它能帮你编写跨版本兼容的测试代码。