如何在PHPUnit的dataProvider执行时初始化测试类成员变量?
PHPUnit 9.x 中让测试类成员在 DataProvider 中可用的解决方案
问题原因
PHPUnit 的执行顺序是数据提供器(dataProvider)先于setUp方法执行,甚至在测试类实例完成初始化前就会被调用。所以通过setUp初始化的成员变量,在dataProvider执行时还未赋值,就会触发「类型属性未初始化」的错误。
解决方案
方案1:直接在属性声明时初始化
如果成员变量的值是固定的,直接在声明时赋值,dataProvider执行时就能访问到:
use PHPUnit\Framework\TestCase; class DataProviderSetupTest extends TestCase { // 直接在属性声明阶段初始化 protected string $state = 'Thank you for the fish!'; /** * @dataProvider statefulDataProvider */ public function testDataProviderWithStateDependency(string $expected): void { $actualResult = 'Thank you for the fish!'; $this->assertSame($expected, $actualResult); } public function statefulDataProvider(): \Generator { yield 'member initialized' => [$this->state]; } }
方案2:使用静态属性 + setUpBeforeClass
如果需要在类初始化阶段(而非每个测试方法前)初始化数据,用静态属性配合setUpBeforeClass静态方法:
use PHPUnit\Framework\TestCase; class DataProviderSetupTest extends TestCase { protected static string $state; public static function setUpBeforeClass(): void { self::$state = 'Thank you for the fish!'; } /** * @dataProvider statefulDataProvider */ public function testDataProviderWithStateDependency(string $expected): void { $actualResult = 'Thank you for the fish!'; $this->assertSame($expected, $actualResult); } public function statefulDataProvider(): \Generator { yield 'static member initialized' => [self::$state]; } }
方案3:在 DataProvider 内部初始化依赖
如果依赖需要动态生成,直接在dataProvider方法里执行初始化逻辑:
use PHPUnit\Framework\TestCase; class DataProviderSetupTest extends TestCase { /** * @dataProvider statefulDataProvider */ public function testDataProviderWithStateDependency(string $expected): void { $actualResult = 'Thank you for the fish!'; $this->assertSame($expected, $actualResult); } public function statefulDataProvider(): \Generator { // 在dataProvider内部初始化所需变量 $state = $this->initializeState(); yield 'dependency initialized in provider' => [$state]; } private function initializeState(): string { return 'Thank you for the fish!'; } }
方案4:通过构造函数初始化成员
如果必须用非静态成员,可以在测试类的构造函数中初始化,这样dataProvider执行时实例已完成初始化:
use PHPUnit\Framework\TestCase; class DataProviderSetupTest extends TestCase { protected string $state; public function __construct(?string $name = null, array $data = [], $dataName = '') { parent::__construct($name, $data, $dataName); // 在构造函数中完成初始化 $this->state = 'Thank you for the fish!'; } /** * @dataProvider statefulDataProvider */ public function testDataProviderWithStateDependency(string $expected): void { $actualResult = 'Thank you for the fish!'; $this->assertSame($expected, $actualResult); } public function statefulDataProvider(): \Generator { yield 'member initialized via constructor' => [$this->state]; } }
注意事项
- 尽量避免在dataProvider中依赖测试类的动态状态,因为dataProvider的执行时机早于测试方法的生命周期
- 使用构造函数初始化时,要确保不会破坏测试方法的隔离性(如果每个测试需要不同的状态,此方式不适用)
内容的提问来源于stack exchange,提问作者k0pernikus
相关产品推荐
相关产品推荐

