TDD 在 PHP 中的完整流程 核心循环Red → Green → Refactor 写一个失败的测试 → 写最少代码让测试通过 → 重构代码---环境准备 composer require--dev phpunit/phpunit phpunit.xml?xml version1.0encodingUTF-8?phpunit bootstrapvendor/autoload.phpcolorstruetestsuitestestsuite nameAppdirectorytests/directory/testsuite/testsuites/phpunit---完整示例购物车1.Red — 先写测试测试会失败// tests/CartTest.phpuse PHPUnit\Framework\TestCase;classCartTestextends TestCase{publicfunctiontest_empty_cart_has_zero_total():void{$cartnewCart();$this-assertSame(0.0,$cart-total());}publicfunctiontest_add_item_updates_total():void{$cartnewCart();$cart-add(apple,1.50,2);$this-assertSame(3.0,$cart-total());}publicfunctiontest_cannot_add_negative_quantity():void{$this-expectException(InvalidArgumentException::class);$cartnewCart();$cart-add(apple,1.50,-1);}}运行 → 全部红./vendor/bin/phpunit#ERRORS:ClassCartnotfound---2.Green — 写最少代码让测试通过// src/Cart.phpclassCart{privatearray $items[];publicfunctionadd(string $name,float$price,int$qty):void{if($qty0){thrownewInvalidArgumentException(Quantity cannot be negative);}$this-items[][price$price,qty$qty];}publicfunctiontotal():float{returnarray_sum(array_map(fn($item)$item[price]*$item[qty],$this-items));}}运行 → 全部绿./vendor/bin/phpunit#OK(3tests,3assertions)---3.Refactor — 改善代码不改行为// src/Cart.php — 重构后classCart{privatearray $items[];publicfunctionadd(string $name,float$price,int$qty):void{if($qty0){thrownewInvalidArgumentException(Quantity cannot be negative);}$this-items[$name]($this-items[$name]??0)($price*$qty);}publicfunctiontotal():float{returnarray_sum($this-items);}}再次运行 → 仍然全部绿重构安全完成。---常用断言速查 ┌────────────────────────────────┬─────────────────────┐ │ 断言 │ 用途 │ ├────────────────────────────────┼─────────────────────┤ │assertSame($a,$b)│ 严格相等类型值 │ ├────────────────────────────────┼─────────────────────┤ │assertEquals($a,$b)│ 宽松相等 │ ├────────────────────────────────┼─────────────────────┤ │ assertTrue/assertFalse │ 布尔值 │ ├────────────────────────────────┼─────────────────────┤ │assertCount($n,$arr)│ 数组长度 │ ├────────────────────────────────┼─────────────────────┤ │expectException(Foo::class)│ 期望异常 │ ├────────────────────────────────┼─────────────────────┤ │assertInstanceOf($class,$obj)│ 类型检查 │ └────────────────────────────────┴─────────────────────┘---Mock 依赖// 被测类依赖外部服务时publicfunctiontest_order_sends_email():void{$mailer$this-createMock(Mailer::class);$mailer-expects($this-once())-method(send)-with($this-stringContains(order));$ordernewOrder($mailer);$order-confirm(userexample.com);}---关键原则-一次只测一件事 — 每个测试方法只验证一个行为-测试命名要描述行为 — test_cannot_add_negative_quantity 而不是 test2-先写测试再写实现 — 不要反过来-测试要快 — 避免在单元测试中访问数据库/网络用 Mock 替代-AAA 结构 — Arrange准备→ Act执行→ Assert断言