phpunit.xml必须位于项目根目录,命名为phpunit.xml或phpunit.xml.dist,大小写严格匹配,且根节点为<phpunit>并含<testsuites>或<bootstrap>子节点,否则PHPUnit将静默回退默认行为。

PHPUnit 是 PHP 单元测试的事实标准,不是“可选工具”,而是你写完第一个 public function calculate() 就该配上的安全带。不装它,等于裸写业务逻辑;装了但不会 mock 依赖、不会测 protected 方法、不理解 phpunit.xml 怎么生效,等于系了安全带却把卡扣掰弯了。
phpunit 命令不识别 phpunit.xml 的真实原因
不是配置文件写得不对,而是 PhpStorm 或 CLI 根本没“看见”它。
- 文件必须放在项目根目录(即
composer.json所在层),名字必须是phpunit.xml或phpunit.xml.dist,大小写不能错 - 右键点击该文件 → 选择 Mark as PHPUnit Configuration File(PhpStorm 2023.3+ 才有此选项;旧版需手动在 Settings → PHP → Test Frameworks 中指定路径)
-
phpunit.xml必须含合法根节点:<phpunit>,且至少包含<testsuites>或<bootstrap>子节点;空文件或仅注释会被静默跳过 - CLI 下运行
vendor/bin/phpunit时,它默认只在当前目录找配置;若你在tests/目录下执行,会找不到根目录的phpunit.xml
mock 数据库查询时,别直接 mock PDO::prepare
用 $this->createMock(PDO::class) 拦截 prepare 看似底层可控,实则极易翻车:PDOStatement 返回值类型难对齐、fetch/fetchAll 行为模拟易漏、事务和错误状态全得手写补全。
- 优先用
getMockBuilder('UserRepository')->getMock(),只 mock 你真正调用的业务方法(如findActiveUsers()),返回数组或对象即可,与 PDO 解耦 - 若必须验证 SQL 字符串生成逻辑,改用
Prophecy:先$repo->findByName('alice')->shouldHaveBeenCalledWith('alice'),再willReturn(...),语义清晰且不碰 PDO 底层 - 内存 SQLite(
sqlite::memory:)适合集成类测试,但注意它不支持外键约束、某些日期函数,且每次setUp()都要重建 schema
测试 private 方法的三种可行路径
别说服自己“private 不该测”——当你发现一个私有方法占了类里 70% 的核心逻辑,又没法通过 public 方法触发全部分支时,你就得直面它。
立即学习“PHP免费学习笔记(深入)”;
- 用
ReflectionMethod:获取方法后调用setAccessible(true),再invoke($instance, $arg1, $arg2);缺点是断言失败时堆栈指向反射调用而非原方法行号 - 把
private改成protected,然后用$this->getMockBuilder()->onlyMethods(['doCalculation'])做 partial mock;这是设计上更健康的方案,也方便未来被子类复用 - 写一个匿名子类覆盖目标方法:
$testObj = new class($realObj) extends TargetClass { public function doCalculation($x) { return $x * 2; } };;无需反射,IDE 可跳转,调试友好
运行单个测试方法报 Class 'TestsTestCase' not found
这不是 PHPUnit 报错,是 Composer 自动加载失效。PhpStorm 调试时尤其高频。
- 检查测试类命名空间是否匹配
composer.json中autoload-dev的映射,例如:"Tests\": "tests/"→ 测试类必须声明namespace TestsUnit; - 文件路径必须严格对应命名空间:类
AddNumbersTest必须存为tests/Unit/AddNumbersTest.php - 改完命名空间或目录结构后,立刻执行
composer dump-autoload -o;不加-o有时仍走缓存 - 绝对不要在测试文件里
require 'TestCase.php'—— 这会让 PhpStorm 在调试时无法解析继承链,断点直接跳进 vendor
最常被忽略的点:mock 对象的 method name 和参数签名必须与真实类完全一致,包括大小写、可选参数顺序、引用符号(&$data)。少一个 &,willReturn() 就永远不触发。



















