PHPUnit是PHP主流单元测试框架,通过Composer安装并配置phpunit.xml,编写遵循3A模式的独立测试用例,使用断言验证代码行为,结合模拟对象隔离依赖,提升代码质量与可维护性,并支持CI/CD集成。

PHPUnit是PHP生态系统中最主流、最权威的单元测试框架,它能帮助开发者通过编写自动化测试用例,验证代码的各个独立部分(单元)是否按预期工作。安装PHPUnit通常通过Composer进行,然后在项目中配置并执行测试文件。掌握PHPUnit不仅能提升代码质量和项目的稳定性,更是现代PHP开发不可或缺的一环。
要安装和使用PHPUnit,你需要确保系统上已经安装了PHP和Composer。
1. 安装PHPUnit
最推荐的方式是将其作为项目的开发依赖安装:
立即学习“PHP免费学习笔记(深入)”;
composer require --dev phpunit/phpunit
这条命令会将PHPUnit安装到你的项目
vendor/
如果你确实需要在全局安装PHPUnit,可以执行:
composer global require phpunit/phpunit
然后确保
~/.composer/vendor/bin
2. 配置PHPUnit
在项目根目录下创建一个名为
phpunit.xml
phpunit.xml.dist
phpunit.xml.dist
phpunit.xml
一个基本的
phpunit.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Application">
<directory>./tests</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>./src</directory>
</include>
</source>
</phpunit>bootstrap="vendor/autoload.php"
colors="true"
<testsuites>
./tests
<source>
3. 编写第一个测试
假设我们有一个简单的
Calculator
src/Calculator.php
<?php
namespace App;
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
public function subtract(int $a, int $b): int
{
return $a - $b;
}
}现在,我们在
tests/
CalculatorTest.php
<?php
namespace Tests;
use App\Calculator;
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase
{
public function testAddNumbers(): void
{
$calculator = new Calculator();
$result = $calculator->add(2, 3);
$this->assertEquals(5, $result);
}
public function testSubtractNumbers(): void
{
$calculator = new Calculator();
$result = $calculator->subtract(5, 2);
$this->assertEquals(3, $result);
}
public function testAddNegativeNumbers(): void
{
$calculator = new Calculator();
$result = $calculator->add(-1, -2);
$this->assertEquals(-3, $result);
}
}PHPUnit\Framework\TestCase
test
@test
$this->assertEquals()
4. 运行测试
在项目根目录的终端中执行:
vendor/bin/phpunit
如果一切顺利,你将看到类似这样的输出:
PHPUnit 10.5.x by Sebastian Bergmann and contributors. .F. 3 / 3 (100%) Time: 00:00.000, Memory: 4.00 MB OK (3 tests, 3 assertions)
这表示你的3个测试都通过了。如果有失败的测试,PHPUnit会详细指出哪个断言失败以及原因。
一开始接触单元测试,很多人可能会觉得它增加了额外的工作量,甚至有点“多此一举”。我刚开始的时候也是这么想的,觉得写业务代码都来不及,哪有时间写测试?但随着项目规模的扩大,以及经历了几次因为小改动导致大问题的“事故”后,我才真正体会到单元测试的价值,它就像给你的代码穿上了一层隐形的“防弹衣”。
提升代码质量与稳定性: 单元测试迫使你对代码的每个小部分进行独立思考,确保它们按照预期工作。这能显著减少bug,尤其是在代码重构或添加新功能时,可以快速发现潜在问题,避免它们蔓延到生产环境。想象一下,一个简单的计算错误可能导致用户资金损失,而单元测试可以在第一时间捕获它。
增强重构信心: 重构是软件开发中常见的活动,但它常常伴随着风险——担心改动会破坏现有功能。有了全面的单元测试,你可以大胆地进行代码优化、结构调整,因为测试套件会像一个忠实的守卫,一旦你的改动引入了问题,它会立刻发出警报。这种安全感是无价的。
优化代码设计: 编写可测试的代码,本身就是一种优秀的设计实践。为了让一个单元容易测试,你不得不将其依赖解耦,使其职责单一。这自然而然地引导你写出更模块化、更清晰、更易于维护的代码。可以说,单元测试是推动良好架构设计的隐形力量。
作为活文档: 测试用例本身就是代码行为的最佳文档。当你阅读一个测试用例时,你可以清楚地了解某个函数或方法在不同输入下的预期输出和行为。这比单独的注释或文档更具时效性和准确性,因为它会随着代码的更新而更新,并且永远不会“过时”。
促进团队协作: 在团队项目中,单元测试提供了一个统一的质量标准。新成员可以通过阅读测试用例快速理解现有代码的功能和设计意图。同时,它也为团队成员间的代码审查提供了依据,确保每个人提交的代码都符合预期的行为。
PHPUnit提供了丰富的断言方法来验证代码行为,这些断言是编写测试用例的核心。高效的测试用例则需要遵循一些原则,才能真正发挥其价值。
常用断言:
$this->assertEquals($expected, $actual, $message = '')
$this->assertNotEquals($expected, $actual)
$this->assertTrue($condition, $message = '')
$this->assertFalse($condition, $message = '')
$this->assertNull($variable, $message = '')
null
$this->assertNotNull($variable)
null
$this->assertEmpty($variable)
0
null
$this->assertNotEmpty($variable)
$this->assertContains($needle, $haystack)
haystack
needle
$this->assertCount($expectedCount, $haystack)
haystack
expectedCount
$this->assertInstanceOf($expectedClass, $actual)
actual
expectedClass
$this->assertStringContainsString($needle, $haystack)
haystack
needle
$this->expectException(\Exception::class)
例如,测试一个除法函数可能抛出零除异常:
public function testDivideByZeroThrowsException(): void
{
$this->expectException(\InvalidArgumentException::class);
$calculator = new Calculator();
$calculator->divide(10, 0); // 假设divide方法会抛出InvalidArgumentException
}编写高效测试用例的策略:
setUp()
tearDown()
test_should_return_sum_of_two_positive_numbers()
testAdd()
@dataProvider
我刚开始写测试的时候,也犯过一些错误,比如一个方法里塞了十几个断言,或者花大量精力去测试一个私有方法。后来才明白,好的测试用例就像一篇简洁的说明书,一眼就能看出代码的功能和预期。它不是为了覆盖率而写,而是为了保障代码行为的正确性。
随着项目的成长,你可能会遇到更复杂的目录结构、不同的测试类型(单元、集成)、以及在不同环境(本地开发、CI/CD)下运行测试的需求。PHPUnit的
phpunit.xml
1. phpunit.xml
<testsuites>
<testsuites>
<testsuite name="Unit">
<directory>./tests/Unit</directory>
</testsuite>
<testsuite name="Integration">
<directory>./tests/Integration</directory>
</testsuite>
</testsuites>这样,你可以通过
vendor/bin/phpunit --testsuite Unit
--testsuite Integration
bootstrap
bootstrap
<phpunit bootstrap="config/bootstrap.php">
<php>
ini
<php>
<ini name="memory_limit" value="256M"/>
<env name="APP_ENV" value="testing"/>
<const name="MY_CONSTANT" value="test_value"/>
</php><source>
php-xdebug
php-pcov
source
<source>
<include>
<directory>./src</directory>
</include>
<exclude>
<directory>./src/Legacy</directory> <!-- 排除旧代码 -->
<file>./src/config.php</file>
</exclude>
</source>运行
vendor/bin/phpunit --coverage-html build/coverage
<logging>
<logging>
<junit outputFile="build/report.junit.xml"/>
<html outputFile="build/report.html"/>
</logging>2. 模拟对象 (Mocks) 和存根 (Stubs):
在复杂的项目中,你的代码往往会依赖于外部服务(如数据库、API、文件系统)或复杂的对象。在单元测试中,我们希望隔离被测试的单元,避免这些外部依赖的影响,以确保测试的快速性、稳定性和独立性。这时,模拟对象和存根就派上用场了。
存根 (Stubs): 存根提供预设的响应,用于满足被测试代码的依赖,但它不关心调用了多少次,也不对调用方式做断言。
// 假设有一个邮件服务接口 MailerInterface
$mailerStub = $this->createStub(MailerInterface::class);
$mailerStub->method('send')
->willReturn(true); // 总是返回true
// 现在你可以用这个存根来测试依赖MailerInterface的类模拟对象 (Mocks): 模拟对象不仅提供预设响应,还会验证对它的调用是否符合预期(例如,某个方法是否被调用了特定次数,或者参数是否正确)。
$mailerMock = $this->createMock(MailerInterface::class);
$mailerMock->expects($this->once()) // 期望send方法被调用一次
->method('send')
->with('test@example.com', 'Hello') // 期望参数是这些
->willReturn(true);
$service = new MyService($mailerMock);
$service->doSomethingThatSendsMail(); // 调用会发送邮件的方法使用模拟对象是隔离复杂依赖、确保单元测试纯粹性的关键技术。
3. CI/CD 集成:
在复杂的项目中,将PHPUnit集成到持续集成/持续部署(CI/CD)流程中是至关重要的。这意味着每次代码提交或合并请求时,CI系统(如Jenkins、GitLab CI、GitHub Actions)都会自动运行你的测试套件。
composer install
vendor/bin/phpunit
phpunit.xml
logging
项目刚开始时,一个简单的
phpunit.xml
以上就是php PHPUnit如何安装和使用?PHPUnit安装与使用教程的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号