如何在PHPUnit的静态数据提供器中创建测试替身?
PHPUnit 10静态数据提供器创建测试替身的解决方案
问题背景
PHPUnit 10弃用了非静态测试数据提供器,原有的非静态提供器中通过$this->createMock()/$this->createStub()创建测试替身的方式会触发 deprecation 警告;若将提供器改为静态,又会因无法使用$this而报错:
There was 1 PHPUnit error: The data provider specified for testFake is invalid. Using $this when not in object context
如果将测试替身移到测试方法内创建,复杂场景下会导致测试方法签名过长,可读性下降。以下是几种可行的解决办法:
方案一:传递替身配置参数,测试方法内统一创建
将测试替身的行为配置(如方法返回值、参数匹配规则)放在静态提供器中,测试方法内通过$this->createMock()创建替身并应用配置,既符合静态要求,又避免签名臃肿:
public static function provideFake(): array { // 每个测试用例传递mock的配置数组 return [ '测试日期格式化返回年初' => [ 'mockConfig' => [ 'format' => [ 'args' => ['Y-m-d'], 'return' => '2024-01-01' ] ] ], '测试日期格式化返回年末' => [ 'mockConfig' => [ 'format' => [ 'args' => ['Y-m-d'], 'return' => '2024-12-31' ] ] ] ]; } /** * @dataProvider provideFake */ public function testFake(array $mockConfig): void { // 在测试方法内创建mock并应用配置 $date = $this->createMock(\DateTime::class); foreach ($mockConfig as $methodName => $config) { $method = $date->method($methodName); if (!empty($config['args'])) { $method->with(...$config['args']); } $method->willReturn($config['return']); } // 执行测试断言 self::assertEquals('2024-01-01', $date->format('Y-m-d')); }
方案二:直接使用PHPUnit MockGenerator静态创建替身
通过直接实例化PHPUnit的Generator类,在静态提供器中创建测试替身,无需依赖TestCase的非静态方法:
use PHPUnit\Framework\MockObject\Generator; public static function provideFake(): array { $generator = new Generator(); // 创建第一个mock并设置行为 $someDate = $generator->getMock( \DateTime::class, [], // 空数组表示模拟所有方法 [], // 构造函数参数(无需传参则留空) '', // Mock类名前缀 true, // 是否调用原始构造函数 true // 是否克隆原始类静态属性 ); $someDate->method('format') ->with('Y-m-d') ->willReturn('2024-01-01'); // 创建第二个mock $someOtherDate = $generator->getMock(\DateTime::class); return [ 'first test' => [$someDate], 'second test' => [$someOtherDate], ]; } /** * @dataProvider provideFake */ public function testFake(\DateTime $date): void { self::assertTrue(true); }
方案三:用匿名类实现轻量测试替身
对于行为简单的场景,直接用PHP匿名类替代PHPUnit的mock工具,完全在静态环境下创建,无需依赖框架方法:
public static function provideFake(): array { $someDate = new class extends \DateTime { public function format(string $format): string { return '2024-01-01'; } }; $someOtherDate = new class extends \DateTime { public function format(string $format): string { return '2024-12-31'; } }; return [ 'first test' => [$someDate], 'second test' => [$someOtherDate], ]; } /** * @dataProvider provideFake */ public function testFake(\DateTime $date): void { self::assertEquals('2024-01-01', $date->format('Y-m-d')); }
内容的提问来源于stack exchange,提问作者Halibut
相关产品推荐
相关产品推荐

