diff --git a/Tests/Fixtures/TestCaseTrait/configTestData.php b/Tests/Fixtures/TestCaseTrait/configTestData.php new file mode 100644 index 0000000..91374ac --- /dev/null +++ b/Tests/Fixtures/TestCaseTrait/configTestData.php @@ -0,0 +1,10 @@ + [ + [ + 'input' => 'foo', + 'expected' => 'bar', + ], + ], +]; diff --git a/Tests/Fixtures/TestCaseTrait/loadTestDataConfig.php b/Tests/Fixtures/TestCaseTrait/loadTestDataConfig.php new file mode 100644 index 0000000..21d0993 --- /dev/null +++ b/Tests/Fixtures/TestCaseTrait/loadTestDataConfig.php @@ -0,0 +1,10 @@ + [ + 'foo' => 'bar', + ], + 'test_data' => [ + 'baz' => 'qux', + ], +]; diff --git a/Tests/Unit/TestCaseTrait/TestCase.php b/Tests/Unit/TestCaseTrait/TestCase.php new file mode 100644 index 0000000..475b636 --- /dev/null +++ b/Tests/Unit/TestCaseTrait/TestCase.php @@ -0,0 +1,18 @@ +assertSame( + [ + [ + 'input' => 'foo', + 'expected' => 'bar', + ], + ], + $this->configTestData() + ); + } + + public function testShouldReturnWholeConfigWhenTestDataKeyIsAbsent() { + $this->config = [ + 'foo' => 'bar', + ]; + + $this->assertSame( [ 'foo' => 'bar' ], $this->configTestData() ); + } + + public function testShouldNotReloadConfigWhenAlreadyPopulated() { + // First call: lazily loads the fixture. + $this->configTestData(); + + // Overwrite with a sentinel value that has no `test_data` key. + $this->config = [ + 'sentinel' => true, + ]; + + // Second call must not reload from the fixture, since $config is no longer empty. + $this->assertSame( [ 'sentinel' => true ], $this->configTestData() ); + } +} diff --git a/Tests/Unit/TestCaseTrait/loadTestDataConfig.php b/Tests/Unit/TestCaseTrait/loadTestDataConfig.php new file mode 100644 index 0000000..eed296a --- /dev/null +++ b/Tests/Unit/TestCaseTrait/loadTestDataConfig.php @@ -0,0 +1,37 @@ +loadTestDataConfig(); + + $this->assertSame( + [ + 'structure' => [ + 'foo' => 'bar', + ], + 'test_data' => [ + 'baz' => 'qux', + ], + ], + $this->config + ); + } + + public function testShouldSetEmptyConfigWhenNoMatchingFixtureFileExists() { + $target = new NoFixtureTestCase(); + + $method = $this->get_reflective_method( 'loadTestDataConfig', NoFixtureTestCase::class ); + $method->invoke( $target ); + + $this->assertSame( [], $this->getNonPublicPropertyValue( 'config', NoFixtureTestCase::class, $target ) ); + } +} diff --git a/src/TestCaseTrait.php b/src/TestCaseTrait.php index 469f1fd..00fbb3b 100644 --- a/src/TestCaseTrait.php +++ b/src/TestCaseTrait.php @@ -7,6 +7,7 @@ use ReflectionClass; use ReflectionException; use ReflectionMethod; +use ReflectionObject; use ReflectionProperty; trait TestCaseTrait { @@ -33,6 +34,38 @@ protected function getTestData( $dir, $filename ) { // phpcs:ignore WordPress.Na : []; } + /** + * Structure + test data configuration, lazily loaded by {@see configTestData()}. + * + * @var array + */ + protected $config = []; + + /** + * Test Data Provider that uses the `'test_data'` key of the config file matching the test class, + * lazily loading it on first use. + * + * @return array + */ + public function configTestData() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Public API method; renaming would be a breaking change for consumers. + if ( empty( $this->config ) ) { + $this->loadTestDataConfig(); + } + + return $this->config['test_data'] ?? $this->config; + } + + /** + * Loads the test data config file matching the current test class name and location. + * + * @return void + */ + protected function loadTestDataConfig() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid -- Public API method; renaming would be a breaking change for consumers. + $file = ( new ReflectionObject( $this ) )->getFileName(); + + $this->config = $this->getTestData( dirname( $file ), basename( $file, '.php' ) ); + } + /** * Get reflective access to the private/protected method. *