引言:为什么 PHP 中需要测试?
\n\n没有测试的 PHP 开发就像没有地基的房屋。迟早应用程序会崩溃,用户将看到“白屏死机”。代码测试不是奢侈品,而是任何严肃项目的必需品。它能够:
\n- \n
- 在早期阶段发现错误 — 在代码进入生产环境之前。 \n
- 简化重构 — 您可以放心地更改架构,因为知道测试会捕获回归问题。 \n
- 创建自文档化代码 — 测试作为系统应如何工作的活文档。 \n
- 增强团队信心 — 每次新提交都会自动通过检查。 \n
PHP 中有三个主要的测试级别:Unit(单元测试)、Integration(集成测试)和 E2E(端到端测试)。在本文中,我们将通过实际示例逐一讲解,从简单的断言到完整的浏览器测试。
\n\n1. 单元测试:测试应用程序的“积木块”
\n\n什么是单元测试?
\n单元测试检查一小段代码的工作情况——通常是一个方法或函数——完全隔离于外部依赖(数据库、API、文件系统)。
\n\nPHP 工具
\n- \n
- PHPUnit — 事实上的标准。用于 Laravel、Symfony 和许多 CMS。 \n
- Pest — 现代替代方案,语法更简洁,构建在 PHPUnit 之上。 \n
示例:测试折扣计算器
\n假设我们有一个简单的类 DiscountCalculator:
<?php\n\nclass DiscountCalculator\n{\n public function calculate(float $price, float $percent): float\n {\n if ($percent < 0 || $percent > 100) {\n throw new \\InvalidArgumentException('百分比必须在 0 到 100 之间');\n }\n return $price - ($price * $percent / 100);\n }\n}\n\n\n现在使用 PHPUnit 编写单元测试:
\n\n<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass DiscountCalculatorTest extends TestCase\n{\n private DiscountCalculator $calculator;\n\n protected function setUp(): void\n {\n $this->calculator = new DiscountCalculator();\n }\n\n public function testCalculateWithZeroPercent(): void\n {\n $result = $this->calculator->calculate(100, 0);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateWithFiftyPercent(): void\n {\n $result = $this->calculator->calculate(200, 50);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateThrowsExceptionOnInvalidPercent(): void\n {\n $this->expectException(\\InvalidArgumentException::class);\n $this->calculator->calculate(100, 150);\n }\n}\n\n\n关键点:
\n- \n
- 每个方法只测试一个场景。 \n
- 我们不访问数据库或外部服务。 \n
- 使用
setUp()初始化公共对象。 \n
单元测试最佳实践
\n- \n
- 遵循 FIRST 原则:快速、隔离、可重复、自我验证、及时。 \n
- 将测试命名为“test_动作_预期结果”。 \n
- 避免在测试中使用逻辑——没有 if/switch/循环。 \n
2. 集成测试:检查组件之间的交互
\n\n与单元测试有何不同?
\n集成测试检查系统的不同部分如何协同工作。例如,您的服务如何与真实数据库、缓存或外部 API 交互。
\n\n示例:测试带数据库的仓库(Laravel + SQLite)
\n假设我们有一个 <