You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:36:02