PHP 8.1 下必须使用 PHPUnit ^10.5,旧版本因移除动态属性和类型收紧而报错;测试方法需声明返回类型、属性须显式定义;断言推荐 use assertSame 避免松散比较陷阱;框架测试须注意启动流程与路径规范。

PHP 8.1 下用 PHPUnit 写测试用例,关键不是“能不能用”,而是必须用 phpunit/phpunit ^10.5 —— 低于这个版本(比如 v9.x)在 PHP 8.1+ 上会直接报 TypeError 或找不到 PHPUnit\Framework\TestCase,因为 PHP 8.1 移除了动态属性支持、收紧了类型推导,旧版 PHPUnit 的反射逻辑会崩。
安装必须指定 ^10.5 版本
Composer 默认可能装低版本,不加约束极易踩坑。PHP 8.1 环境下只认 ^10.5 及以上:
- 运行
composer require --dev phpunit/phpunit ^10.5,不要省略^10.5 - 检查
composer.json中 dev 依赖是否为"phpunit/phpunit": "^10.5",不是"^9.6"或空版本号 - 运行
./vendor/bin/phpunit --version,确认输出类似PHPUnit 10.5.2 by Sebastian Bergmann - 若报错
Class "PHPUnit\Framework\TestCase" not found,大概率是版本不匹配或 autoloader 没加载到 vendor/autoload.php
测试类写法要适配 PHP 8.1 的严格性
PHP 8.1 强制要求返回类型声明、禁止动态属性,测试类里容易漏掉几个关键点:
- 测试方法签名必须带返回类型,例如
public function testAdd(): void,不能省略: void - 不能在测试类里给未声明的属性赋值,比如
$this->calc = new Calculator();前没声明private Calculator $calc;,PHP 8.1 会抛Error: Cannot access uninitialized non-nullable property - 推荐用构造注入或
setUp(): void初始化,且所有属性需显式声明类型 - 如果用
@dataProvider,参数类型必须与提供器数组元素严格一致,例如提供器返回[2, 3, 5],测试方法就得写成testAdd(int $a, int $b, int $expected): void
断言和异常测试要避开 PHP 8.1 的类型陷阱
PHP 8.1 对 assertEquals 的松散比较更敏感,尤其涉及字符串和数字混用时:
立即学习“PHP免费学习笔记(深入)”;
-
$this->assertEquals('5', 5)在 PHP 8.1 + PHPUnit 10.5 下默认失败(严格模式),改用$this->assertSame('5', 5)显式表达意图,或用$this->assertEquals(5, (int) '5') - 测试异常必须用
@expectException注解配合完整命名空间,例如/** @expectException InvalidArgumentException */,写/** @expectException InvalidArgumentException */不够,得是/** @expectException InvalidArgumentException */(注意大小写与命名空间) - 若被测代码抛出
TypeError,PHPUnit 10.5 支持捕获,但需写全名:/** @expectException TypeError */
ThinkPHP/Laravel 等框架测试要绕过容器初始化陷阱
在 PHP 8.1 下,框架测试失败常不是 PHPUnit 问题,而是容器或启动流程没走完:
- ThinkPHP 8 测试必须继承
think\testing\TestCase,且createApplication()返回include __DIR__ . '/../thinkphp/start.php';直接new App()会导致$this->db为 null - Laravel 测试若用
RefreshDatabasetrait,确保phpunit.xml中bootstrap指向正确,否则迁移命令不执行,assertDatabaseHas永远查不到数据 - 所有框架测试文件必须放在项目根目录
tests/下,不能塞进app/或src/,否则 PHPUnit 扫不到——PHP 8.1 不再容忍路径模糊匹配
最易被忽略的是:PHP 8.1 默认启用 strict_types=1 后,phpunit.xml 里的 bootstrap 文件若没声明 declare(strict_types=1);,而测试类又用了严格类型,就会出现“类型声明冲突”却无明确报错——建议整个测试 bootstrap 链路统一加这行声明。



















