PHPUnit测试中数字字符串传入抽象类int参数未触发TypeError原因求解
PHP8 环境下测试抽象类构造函数参数类型校验异常不触发问题解析
相关代码
抽象类定义
declare(strict_types=1); abstract class IntValueObject { public function __construct(protected int $value) { } }
初始测试用例
declare(strict_types=1); final class IntValueObjectTest extends TestCase { public function testWithNotValidValue(): void { $value = '1'; $this->expectException(\TypeError::class); $this->getMockForAbstractClass(IntValueObject::class, [$value]); } }
问题现象
运行上述测试后返回报错:
Api\Tests\Shared\Domain\ValueObject\IntValueObjectTest::testWithNotValidValue
Failed asserting that exception of type "TypeError" is thrown.
另外验证发现两个特殊表现:
- 将
$value的值从'1'改为'foo'后,测试可正常通过 - 同样传入
'1'的场景,PHP8 生产环境会正常抛出TypeError,仅测试环境不触发
问题原因
该问题属于PHP内核已知缺陷:使用PHPUnit的getMockForAbstractClass方法创建抽象类模拟对象时,底层参数处理逻辑会绕过严格类型声明的校验规则,对数值格式的字符串(如'1')会做隐式类型转换为int,不会触发严格模式下的TypeError;只有传入完全无法转为数字的字符串(如'foo')时才会抛出类型异常。
解决方案
不要使用getMockForAbstractClass实例化抽象类进行该场景测试,改用匿名类继承抽象类的方式实现,修改后的测试用例如下:
declare(strict_types=1); final class IntValueObjectTest extends TestCase { public function testWithNotValidValue(): void { $value = '1'; $this->expectException(\TypeError::class); new class($value) extends IntValueObject {}; } }
匿名类实例化会完全遵循严格类型声明的校验规则,传入'1'时会正常抛出TypeError,测试可正常通过。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

