为什么要学 PHPUnit?
在上一篇文章里,我们聊到了「还没开始单元测试的你一定很忙」,同时也鼓励大家「一个人就能开始测试」。那么接下来就要进入更实际的操作层面,带你走进 PHP 最常见的测试框架 —— PHPUnit。
很多人对 PHPUnit 的印象是「很复杂、很多设定档、好多断言(assert)方法」。但事实上,只要掌握几个核心概念,就能在短时间内写出第一支测试。就像所有新事物一样,最难的往往是那「踏出第一步」。我们就一起从 0 开始,带你做到 1!
为什么选择 PHPUnit?
社群资源丰富
PHPUnit 在 PHP 生态系已耕耘多年,有大量的教学资源与社群讨论,一遇到问题很容易查询到解法。
与框架或纯 PHP 都相容
无论是 Laravel 等主流框架,或是纯 PHP 专案都可以整合 PHPUnit,相当弹性。
Composer 管理轻鬆安装
PHPUnit 的安装与升级都能透过 Composer 完成,对于现代 PHP 专案来说相当方便。
虽然 PHPUnit 很主流,但也不是唯一选择。市面上还有像 Pest、Codeception、PHPSpec 等。不同框架、不同专案需求不一,最终还是看你的开发生态做选择。这里先以 PHPUnit 作为「从 0 到 1」的首选示范。
安装与初始化
建立或确认你的专案环境
- 先準备好一个 PHP 专案目录(举例:my-php-project),并确保你的电脑已经安装 PHP 与 Composer。
透过 Composer 安装 PHPUnit
在专案目录下,执行下列指令:
composer require --dev phpunit/phpunit
- -dev 参数代表这是开发时期使用的套件,不会在正式环境上线时被引入(或会以不同方式处理)。
检查安装成功
安装完成后,你可以在终端机执行:
vendor/bin/phpunit --version
若能看到 PHPUnit 的版本号,表示安装成功。
写下你的第一个测试
建立测试资料夹结构
通常我们会在专案里建立一个 tests 资料夹,来放所有测试程式码。
- 结构示例:
my-php-project/
├── src/
| └── MyCalculator.php
├── tests/
| └── MyCalculatorTest.php
└── composer.json
写个简单的功能:MyCalculator (示范)
假设我们有一个简单的类别 MyCalculator,负责做加法、减法。
<?php
// 档案路径: src/MyCalculator.php
namespace App;
class MyCalculator
{
public function add($a, $b)
{
return $a + $b;
}
public function sub($a, $b)
{
return $a - $b;
}
}
建立测试档 MyCalculatorTest
<?php
// 档案路径: tests/MyCalculatorTest.php
use PHPUnit\\Framework\\TestCase;
use App\\MyCalculator;
class MyCalculatorTest extends TestCase
{
public function testAdd()
{
// Arrange
$calc = new MyCalculator();
// Act
$result = $calc->add(2, 3);
// Assert
$this->assertEquals(5, $result, \'2 + 3 应该等于 5\');
}
public function testSub()
{
$calc = new MyCalculator();
$result = $calc->sub(5, 2);
$this->assertEquals(3, $result, \'5 - 2 应该等于 3\');
}
}
这支测试分为三个常见步骤:
- Arrange(初始化/準备):建立或準备要测试的物件、参数。
- Act(执行):呼叫我们要测试的方法。
- Assert(断言):检查结果是否符合预期。
执行测试
回到终端机,在专案根目录执行:
vendor/bin/phpunit tests
(或仅输入 vendor/bin/phpunit,它会自动寻找 tests 资料夹。)
如果一切顺利,你会在终端机看到类似:
PHPUnit x.y.z by Sebastian Bergmann and contributors.
.. 2 / 2 (100%)
OK (2 tests, 2 assertions)
这代表你的两个测试(testAdd 和 testSub)都通过了!
此时,你已「举出一个实例」来证明自己成功写了测试,也验证了程式运作正常。这份信心很重要,因为很多人都卡在「不知道怎么开始」。
反例示范:测试失败时该怎么办?
为了让你感受「测试失败会长什么样子」,我们故意改一下 MyCalculator::sub():
public function sub($a, $b)
{
return $a + $b; // 故意改错
}
再执行一次测试,你可能会看到:
There was 1 failure:
1) MyCalculatorTest::testSub
Failed asserting that 7 matches expected 3.
这行讯息就很直接地告诉你,预期应该得到 3,实际却是 7,于是测试失败。如此一来,你能在开发阶段就抓到错误,而不必等到功能上线或被 QA 测出来。
更多 PHPUnit 常见断言
这只是 PHPUnit 常见断言的冰山一角,它的功能非常丰富。未来若有更复杂的测试场景(例:例外抛出、时间相关测试、mock 物件等),你都可以在官方文件或社群中找到相应做法。
测试与程式设计的良性循环
在写测试的过程中,往往能促使我们写出更结构化的程式码,因为「要测试,就得让程式码更容易被呼叫、被拆分」。这是一个良性循环:
- 写测试 → 想要「好测」,于是去「重构程式、拆成小模组」。
- 程式结构更好 → 写更多测试也更轻鬆。
- 好测试 → 开发速度与稳定度都提升。
就算是小专案,也能透过 PHPUnit 改善品质,让你对自己的程式更有信心。
结论与后续
恭喜你!如果你跟着这篇示范,成功跑出你的第一个「单元测试」,那就已经从 0 来到 1 了。
- 只要你的测试档能正常跑通,就足以证明「你已经会写单元测试」。
- 要做到大范围测试覆盖、处理更复杂的业务逻辑,还需要不断学习与演进。这篇文章只是带你踏出第一步。
在下一篇,我们将带领大家进一步进入 Laravel Test 实战。利用 Laravel 已经内建的强大测试工具和辅助方法,让你在框架环境下更轻鬆地测试 Controller、路由、资料库操作等。敬请期待!
本文重点回顾
下一篇(Laravel Test 实战:与框架结合的测试技巧)见!一起继续把测试应用到更真实的专案情境。
更多实用开发技巧在:詹姆士的软件易开罐