symfony单元测试的核心是用phpunit验证单个类行为,不依赖数据库或外部服务,需隔离代码、用mock替换依赖、专注逻辑正确性;区分单元测试(测方法输入输出)与功能测试(模拟http请求)。

Symfony单元测试的核心是用 PHPUnit 验证单个类(如服务、实体、DTO)的行为是否符合预期,不依赖数据库、HTTP 请求或外部服务。重点在于隔离被测代码、用 Mock 替换依赖、专注逻辑正确性。
选对测试类型:单元测试 ≠ 功能测试
Symfony 项目中常混用几种测试:
-
单元测试(Unit Test):测试单个 PHP 类的方法,比如
User::isEligibleForDiscount()是否在年龄 ≥ 65 时返回true;使用PHPUnit\Framework\TestCase,禁止调用 Doctrine、Kernel 或 HTTP 客户端。 -
功能测试(Functional Test):通过
WebTestCase模拟 HTTP 请求,验证控制器、路由、表单等端到端行为,会启动轻量内核,可访问服务容器。 - 集成测试、API 测试等属于更高层级,不在单元测试范畴内。
写单元测试前先确认:你只想测一个方法的输入输出逻辑?如果是,就别加载 Kernel,也别写 $client->request()。
创建标准单元测试文件
按 Symfony 推荐结构,测试类放在 tests/ 下,路径与源码对应:
- 源码:
src/Service/PriceCalculator.php - 测试:
tests/Service/PriceCalculatorTest.php
基础模板如下:
namespace App\Tests\Service;
<p>use App\Service\PriceCalculator;
use PHPUnit\Framework\TestCase;</p><p>class PriceCalculatorTest extends TestCase
{
public function testCalculatesTotalWithVat(): void
{
$calculator = new PriceCalculator();
$result = $calculator->calculate(100, 0.2);</p><pre class="brush:php;toolbar:false;"> $this->assertSame(120.0, $result);
}}
注意命名规范:test+描述性动词短语,方法必须是 public 且以 test 开头或加 @test 注解。
用 Mock 隔离外部依赖
当被测类依赖其他服务(如日志、API 客户端、仓储接口),需用 PHPUnit 的 Mock 创建“假对象”:
public function testSendsNotificationOnOrderSuccess(): void
{
$mailer = $this->createMock(MailerInterface::class);
$mailer->expects($this->once())
->method('send')
->with($this->isInstanceOf(EmailMessage::class));
<pre class="brush:php;toolbar:false;">$orderProcessor = new OrderProcessor($mailer);
$orderProcessor->process(new Order('ORD-001'));
// 断言已触发 send 方法,但不真发邮件}
关键点:
-
createMock()生成接口/类的空实现; -
expects()描述调用次数和方式; - 永远避免在单元测试里 new 真实的外部服务(如
new HttpClient())。
运行与调试技巧
终端执行命令即可运行:
php bin/phpunit tests/Service/PriceCalculatorTest.php # 或只跑某个方法 php bin/phpunit --filter testCalculatesTotalWithVat
常见提速建议:
- 禁用 Xdebug(尤其在 CI 中),否则 PHPUnit 速度可能下降 3–5 倍;
- 在
phpunit.xml.dist中配置bootstrap="tests/bootstrap.php"来提前加载 Autoloader; - 用
--verbose查看哪个测试慢,用--debug跟踪执行流程。
不复杂但容易忽略。











