1、单元测试基础:什么是单元测试、为什么需要单元测试、PHPUnit简介与安装
各位同学,咱们今天聊聊单元测试。说实话,我刚开始写PHP那会儿,也觉得测试是浪费时间。直到有一次,我负责的一个电商项目上线前,手动改了一个支付接口的参数,结果把整个订单流程搞崩了。那天晚上我盯着屏幕到凌晨三点,一边改bug一边骂自己——为什么不写测试?
从那以后,我成了测试驱动开发的忠实信徒。今天这第一课,咱们就把单元测试的底裤扒干净,看看它到底是什么玩意儿,为什么非学不可。
什么是单元测试?
单元测试,说白了就是给代码做「体检」。你写了一个函数、一个方法,或者一个类,单元测试就是单独拎出来,检查它能不能按预期工作。
举个例子。你写了个计算折扣的函数:
function calculateDiscount($price, $isVip) {
if ($isVip) {
return $price * 0.8;
}
return $price * 0.9;
}
单元测试会这样测:
// 测试普通用户
$result = calculateDiscount(100, false);
assert($result === 90); // 打九折,应该是90
// 测试VIP用户
$result = calculateDiscount(100, true);
assert($result === 80); // 打八折,应该是80
嗯,就是这么简单。每个测试只关注一个「单元」,不依赖数据库、不依赖外部API、不依赖其他类。我习惯把单元测试想象成「隔离审查」——只审这一个模块,其他的一概不管。
核心要点:单元测试的「单元」通常是一个方法或函数。它应该是独立的、可重复的、自动化的。
为什么需要单元测试?
你可能会问:「我手动测一下不就行了?干嘛费劲写代码测代码?」
好问题。我跟你讲讲我的血泪史。
我曾经维护过一个老项目,代码量大概20万行。每次改一个地方,都要手动跑一遍所有功能。改个登录,要测注册、测找回密码、测权限……一天下来,真正写代码的时间不到两小时,全在点鼠标了。
单元测试能解决三个核心痛点:
- 回归测试自动化——改完代码,跑一遍测试就知道有没有搞坏别的地方。不用再手动点来点去。
- 代码设计更合理——你想想看,如果一个方法很难写测试,那它八成设计得有问题。耦合太高、职责不单一,这些都是坏味道。
- 文档即代码——测试用例本身就是最好的使用文档。新同事接手,看看测试就知道这个类该怎么用、边界条件是什么。
我的经验:我曾经接手过一个项目,没有一行测试代码。我花了整整两周,才敢动第一个文件。后来我强制团队写单元测试,三个月后,发布新版本的效率提升了至少三倍。
还有一个很重要的点——信心。有单元测试兜底,你改代码的时候心里有底。没有测试,每次上线都像在走钢丝。你想想看,哪个更舒服?
PHPUnit简介
PHPUnit是PHP生态里最主流的单元测试框架。它由Sebastian Bergmann创建,目前已经发展到第10版。我用了快十年,从3.x版本一路跟过来,看着它越来越成熟。
PHPUnit能做什么?
- 自动发现并运行测试
- 提供丰富的断言方法(assertEquals、assertTrue、assertNull等)
- 支持测试夹具(setUp、tearDown)
- 支持数据提供器(@dataProvider)
- 生成代码覆盖率报告
- 支持模拟对象(Mock对象)
说白了,PHPUnit就是给你搭好了架子,你只需要往里填测试逻辑就行。
安装PHPUnit
安装方式有两种,我建议你用Composer,这是目前最规范的做法。
方式一:全局安装(推荐)
composer global require phpunit/phpunit
装完之后,在终端里输入 phpunit --version,能看到版本号就说明成功了。
方式二:项目内安装
composer require --dev phpunit/phpunit
这种方式会把PHPUnit装到项目的vendor目录下,通过 ./vendor/bin/phpunit 来运行。
注意:我曾经遇到过一个问题——全局安装的PHPUnit版本和项目依赖的版本冲突。后来我统一用项目内安装,配合composer.json锁定版本,再也没出过问题。建议你也这么做。
装好之后,咱们写个最简单的测试验证一下:
<?php
use PHPUnit\Framework\TestCase;
class FirstTest extends TestCase
{
public function testAddition()
{
$result = 1 + 1;
$this->assertEquals(2, $result);
}
}
保存为 FirstTest.php,然后运行:
./vendor/bin/phpunit FirstTest.php
看到绿色的「OK (1 test, 1 assertion)」了吗?恭喜你,第一个单元测试跑通了。
PHPUnit的核心概念
咱们快速过一下几个关键概念,后面会详细展开:
| 概念 | 说明 |
|---|---|
| 测试用例 | 继承TestCase的类,包含多个测试方法 |
| 测试方法 | 以test开头的方法,包含断言逻辑 |
| 断言 | assertEquals、assertTrue等,验证预期结果 |
| 测试夹具 | setUp和tearDown,在每个测试前后执行 |
| 数据提供器 | @dataProvider注解,用多组数据跑同一个测试 |
嗯,这里要注意——测试方法必须是public的,而且不能有参数(除非用了数据提供器)。我刚开始学的时候,老忘记加public,结果测试跑不起来,找了半天才发现。
小结
今天咱们把单元测试的底子打好了。你知道了:
- 单元测试是给代码做「体检」,每个测试只关注一个单元
- 它能帮你做回归测试、改善代码设计、充当活文档
- PHPUnit是PHP界最主流的测试框架,用Composer安装最靠谱
下一章,咱们会深入讲怎么写第一个真正的测试用例,包括断言怎么写、测试怎么组织。到时候我会拿一个真实项目里的例子来演示,保证你学完就能用上。
记住一句话:没有测试的代码,就像没有安全带的赛车——跑得快,但死得也快。
公众号:蓝海资料掘金营,微信deep3321