From 8782d66d6621475f13dff23741fdecb4e4289646 Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Tue, 19 Mar 2024 18:42:51 +0100 Subject: [PATCH] Allow kernel configuration via attributes (#11) --- CHANGELOG.md | 4 ++ README.md | 28 ++++++++ src/Test/Attribute/ConfigureContainer.php | 23 +++++++ src/Test/Attribute/ConfigureExtension.php | 24 +++++++ src/Test/Attribute/KernelConfiguration.php | 11 ++++ src/Test/Attribute/RegisterBundle.php | 24 +++++++ src/Test/Attribute/RegisterCompilerPass.php | 27 ++++++++ src/Test/ConfigurableKernelTestCase.php | 27 +++++++- src/Test/Reflection/TestAttributeProvider.php | 49 ++++++++++++++ tests/Functional/CompilerPassTest.php | 35 +++++++++- ...est.php => ContainerConfigurationTest.php} | 65 +++++++++++-------- .../Functional/ExtensionConfigurationTest.php | 47 ++++++++++++++ 12 files changed, 334 insertions(+), 30 deletions(-) create mode 100644 src/Test/Attribute/ConfigureContainer.php create mode 100644 src/Test/Attribute/ConfigureExtension.php create mode 100644 src/Test/Attribute/KernelConfiguration.php create mode 100644 src/Test/Attribute/RegisterBundle.php create mode 100644 src/Test/Attribute/RegisterCompilerPass.php create mode 100644 src/Test/Reflection/TestAttributeProvider.php rename tests/Functional/{BundleConfigurationTest.php => ContainerConfigurationTest.php} (50%) create mode 100644 tests/Functional/ExtensionConfigurationTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index b2575d7..fc623e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## v0.12.1 +### Features: +- Allow kernel configuration via attributes + ## v0.12.0 ### Features: - Dynamically configurable test kernel with which you can register bundles, load configurations, diff --git a/README.md b/README.md index 849796f..223e915 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,34 @@ class SomeTest extends ConfigurableKernelTestCase } ``` +An alternative to passing a `config` closure in the `options` array to `ConfigurableKernelTestCase::bootKernel()` +is to use attributes for the kernel configuration. + +```php +use Neusta\Pimcore\TestingFramework\Test\Attribute\ConfigureContainer; +use Neusta\Pimcore\TestingFramework\Test\Attribute\ConfigureExtension; +use Neusta\Pimcore\TestingFramework\Test\Attribute\RegisterBundle; +use Neusta\Pimcore\TestingFramework\Test\Attribute\RegisterCompilerPass; +use Neusta\Pimcore\TestingFramework\Test\ConfigurableKernelTestCase; + +#[RegisterBundle(SomeBundle::class)] +class SomeTest extends ConfigurableKernelTestCase +{ + #[ConfigureContainer(__DIR__ . '/Fixtures/some_config.yaml')] + #[ConfigureExtension('some_extension', ['config' => 'values'])] + #[RegisterCompilerPass(new SomeCompilerPass())] + public function test_something(): void + { + self::bootKernel(); + + // test something + } +} +``` + +> [!TIP] +> All attributes can be used on class *and* test method level. + ### Integration Tests With a Database If you write integration tests that use the database, we've got you covered too. diff --git a/src/Test/Attribute/ConfigureContainer.php b/src/Test/Attribute/ConfigureContainer.php new file mode 100644 index 0000000..4c6879e --- /dev/null +++ b/src/Test/Attribute/ConfigureContainer.php @@ -0,0 +1,23 @@ +addTestConfig($this->config); + } +} diff --git a/src/Test/Attribute/ConfigureExtension.php b/src/Test/Attribute/ConfigureExtension.php new file mode 100644 index 0000000..1bd175a --- /dev/null +++ b/src/Test/Attribute/ConfigureExtension.php @@ -0,0 +1,24 @@ +> $extensionConfig + */ + public function __construct( + private readonly string $namespace, + private readonly array $extensionConfig, + ) { + } + + public function configure(TestKernel $kernel): void + { + $kernel->addTestExtensionConfig($this->namespace, $this->extensionConfig); + } +} diff --git a/src/Test/Attribute/KernelConfiguration.php b/src/Test/Attribute/KernelConfiguration.php new file mode 100644 index 0000000..cee4bba --- /dev/null +++ b/src/Test/Attribute/KernelConfiguration.php @@ -0,0 +1,11 @@ + $bundle + */ + public function __construct( + private readonly string $bundle, + ) { + } + + public function configure(TestKernel $kernel): void + { + $kernel->addTestBundle($this->bundle); + } +} diff --git a/src/Test/Attribute/RegisterCompilerPass.php b/src/Test/Attribute/RegisterCompilerPass.php new file mode 100644 index 0000000..2b73068 --- /dev/null +++ b/src/Test/Attribute/RegisterCompilerPass.php @@ -0,0 +1,27 @@ +addTestCompilerPass($this->compilerPass, $this->type, $this->priority); + } +} diff --git a/src/Test/ConfigurableKernelTestCase.php b/src/Test/ConfigurableKernelTestCase.php index c25213d..5591085 100644 --- a/src/Test/ConfigurableKernelTestCase.php +++ b/src/Test/ConfigurableKernelTestCase.php @@ -4,20 +4,45 @@ namespace Neusta\Pimcore\TestingFramework\Test; use Neusta\Pimcore\TestingFramework\Kernel\TestKernel; +use Neusta\Pimcore\TestingFramework\Test\Attribute\KernelConfiguration; +use Neusta\Pimcore\TestingFramework\Test\Reflection\TestAttributeProvider; use Pimcore\Test\KernelTestCase; abstract class ConfigurableKernelTestCase extends KernelTestCase { + /** @var list */ + private static iterable $kernelConfigurations = []; + /** - * @param array $options + * @param array{config?: callable(TestKernel):void, environment?: string, debug?: bool, ...} $options */ protected static function createKernel(array $options = []): TestKernel { $kernel = parent::createKernel($options); \assert($kernel instanceof TestKernel); + foreach (self::$kernelConfigurations as $configuration) { + $configuration->configure($kernel); + } + $kernel->handleOptions($options); return $kernel; } + + /** + * @internal + * + * @before + */ + public function _getKernelConfigurationFromAttributes(): void + { + self::$kernelConfigurations = (new TestAttributeProvider($this))->getKernelConfigurationAttributes(); + } + + protected function tearDown(): void + { + self::$kernelConfigurations = []; + parent::tearDown(); + } } diff --git a/src/Test/Reflection/TestAttributeProvider.php b/src/Test/Reflection/TestAttributeProvider.php new file mode 100644 index 0000000..e688e99 --- /dev/null +++ b/src/Test/Reflection/TestAttributeProvider.php @@ -0,0 +1,49 @@ +test = new \ReflectionMethod($test, $test->getName(false)); + } + + /** + * @return list + */ + public function getKernelConfigurationAttributes(): array + { + $attributes = []; + foreach ($this->getAttributes($this->test->getDeclaringClass(), KernelConfiguration::class) as $attribute) { + $attributes[] = $attribute->newInstance(); + } + + foreach ($this->getAttributes($this->test, KernelConfiguration::class) as $attribute) { + $attributes[] = $attribute->newInstance(); + } + + return $attributes; + } + + /** + * @template T of object + * + * @param class-string $attribute + * + * @return iterable<\ReflectionAttribute> + */ + private function getAttributes(\ReflectionClass|\ReflectionMethod $source, string $attribute): iterable + { + yield from $source->getAttributes($attribute, \ReflectionAttribute::IS_INSTANCEOF); + } +} diff --git a/tests/Functional/CompilerPassTest.php b/tests/Functional/CompilerPassTest.php index 2111382..687dc61 100644 --- a/tests/Functional/CompilerPassTest.php +++ b/tests/Functional/CompilerPassTest.php @@ -4,6 +4,7 @@ namespace Neusta\Pimcore\TestingFramework\Tests\Functional; use Neusta\Pimcore\TestingFramework\Kernel\TestKernel; +use Neusta\Pimcore\TestingFramework\Test\Attribute\RegisterCompilerPass; use Neusta\Pimcore\TestingFramework\Test\ConfigurableKernelTestCase; use Neusta\Pimcore\TestingFramework\Tests\Fixtures\ConfigurationBundle\DependencyInjection\Compiler\DeregisterSomethingPass; use Neusta\Pimcore\TestingFramework\Tests\Fixtures\ConfigurationBundle\DependencyInjection\Compiler\RegisterSomethingPass; @@ -22,7 +23,7 @@ public function compiler_pass_priority(): void $kernel->addTestCompilerPass(new RegisterSomethingPass()); }]); - $this->assertTrue(self::getContainer()->has('something')); + self::assertTrue(self::getContainer()->has('something')); // Case 2: Compiler pass with priority - it should be prioritized by priority self::bootKernel(['config' => function (TestKernel $kernel) { @@ -30,7 +31,7 @@ public function compiler_pass_priority(): void $kernel->addTestCompilerPass(new RegisterSomethingPass()); }]); - $this->assertFalse(self::getContainer()->has('something')); + self::assertFalse(self::getContainer()->has('something')); // Case 3: Compiler pass without priority - it should be prioritized by order of addition self::bootKernel(['config' => function (TestKernel $kernel) { @@ -39,6 +40,36 @@ public function compiler_pass_priority(): void $kernel->addTestCompilerPass(new DeregisterSomethingPass()); }]); + self::assertFalse(self::getContainer()->has('something')); + } + + /** + * @test + */ + #[RegisterCompilerPass(new DeregisterSomethingPass())] + #[RegisterCompilerPass(new RegisterSomethingPass())] + public function compiler_passes_via_attributes(): void + { + $this->assertTrue(self::getContainer()->has('something')); + } + + /** + * @test + */ + #[RegisterCompilerPass(new DeregisterSomethingPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -5)] + #[RegisterCompilerPass(new RegisterSomethingPass())] + public function compiler_passes_with_priority_via_attributes(): void + { + $this->assertFalse(self::getContainer()->has('something')); + } + + /** + * @test + */ + #[RegisterCompilerPass(new RegisterSomethingPass())] + #[RegisterCompilerPass(new DeregisterSomethingPass())] + public function compiler_passes_without_priority_via_attributes(): void + { $this->assertFalse(self::getContainer()->has('something')); } } diff --git a/tests/Functional/BundleConfigurationTest.php b/tests/Functional/ContainerConfigurationTest.php similarity index 50% rename from tests/Functional/BundleConfigurationTest.php rename to tests/Functional/ContainerConfigurationTest.php index 74c64e6..1322708 100644 --- a/tests/Functional/BundleConfigurationTest.php +++ b/tests/Functional/ContainerConfigurationTest.php @@ -4,32 +4,16 @@ namespace Neusta\Pimcore\TestingFramework\Tests\Functional; use Neusta\Pimcore\TestingFramework\Kernel\TestKernel; +use Neusta\Pimcore\TestingFramework\Test\Attribute\ConfigureContainer; +use Neusta\Pimcore\TestingFramework\Test\Attribute\RegisterBundle; use Neusta\Pimcore\TestingFramework\Test\ConfigurableKernelTestCase; use Neusta\Pimcore\TestingFramework\Tests\Fixtures\ConfigurationBundle\ConfigurationBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; -final class BundleConfigurationTest extends ConfigurableKernelTestCase +#[RegisterBundle(ConfigurationBundle::class)] +final class ContainerConfigurationTest extends ConfigurableKernelTestCase { - /** - * @test - */ - public function extension_configuration(): void - { - self::bootKernel(['config' => function (TestKernel $kernel) { - $kernel->addTestBundle(ConfigurationBundle::class); - $kernel->addTestExtensionConfig('configuration', [ - 'foo' => 'value1', - 'bar' => ['value2', 'value3'], - ]); - }]); - - $container = self::getContainer(); - - $this->assertEquals('value1', $container->getParameter('configuration.foo')); - $this->assertEquals(['value2', 'value3'], $container->getParameter('configuration.bar')); - } - public function provideDifferentConfigurationFormats(): iterable { yield 'YAML' => [__DIR__ . '/../Fixtures/Resources/ConfigurationBundle/config.yaml']; @@ -52,15 +36,42 @@ public function provideDifferentConfigurationFormats(): iterable */ public function different_configuration_formats(string|callable $config): void { - self::bootKernel(['config' => function (TestKernel $kernel) use ($config) { - $kernel->addTestBundle(ConfigurationBundle::class); - $kernel->addTestConfig($config); - }]); + self::bootKernel(['config' => fn (TestKernel $kernel) => $kernel->addTestConfig($config)]); - $container = self::getContainer(); + self::assertContainerConfiguration(self::getContainer()); + } - $this->assertEquals('value1', $container->getParameter('configuration.foo')); - $this->assertEquals(['value2', 'value3'], $container->getParameter('configuration.bar')); + /** + * @test + */ + #[ConfigureContainer(__DIR__ . '/../Fixtures/Resources/ConfigurationBundle/config.yaml')] + public function configuration_in_yaml_via_attribute(): void + { + self::assertContainerConfiguration(self::getContainer()); + } + + /** + * @test + */ + #[ConfigureContainer(__DIR__ . '/../Fixtures/Resources/ConfigurationBundle/config.xml')] + public function configuration_in_xml_via_attribute(): void + { + self::assertContainerConfiguration(self::getContainer()); + } + + /** + * @test + */ + #[ConfigureContainer(__DIR__ . '/../Fixtures/Resources/ConfigurationBundle/config.php')] + public function configuration_in_php_via_attribute(): void + { + self::assertContainerConfiguration(self::getContainer()); + } + + public static function assertContainerConfiguration(ContainerInterface $container): void + { + self::assertEquals('value1', $container->getParameter('configuration.foo')); + self::assertEquals(['value2', 'value3'], $container->getParameter('configuration.bar')); self::assertInstanceOf(\stdClass::class, $container->get('something', ContainerInterface::NULL_ON_INVALID_REFERENCE)); } } diff --git a/tests/Functional/ExtensionConfigurationTest.php b/tests/Functional/ExtensionConfigurationTest.php new file mode 100644 index 0000000..ea5d97f --- /dev/null +++ b/tests/Functional/ExtensionConfigurationTest.php @@ -0,0 +1,47 @@ + function (TestKernel $kernel) { + $kernel->addTestBundle(ConfigurationBundle::class); + $kernel->addTestExtensionConfig('configuration', [ + 'foo' => 'value1', + 'bar' => ['value2', 'value3'], + ]); + }]); + + $container = self::getContainer(); + + self::assertEquals('value1', $container->getParameter('configuration.foo')); + self::assertEquals(['value2', 'value3'], $container->getParameter('configuration.bar')); + } + + /** + * @test + */ + #[RegisterBundle(ConfigurationBundle::class), ConfigureExtension('configuration', [ + 'foo' => 'value1', + 'bar' => ['value2', 'value3'], + ])] + public function extension_configuration_via_attributes(): void + { + $container = self::getContainer(); + + self::assertEquals('value1', $container->getParameter('configuration.foo')); + self::assertEquals(['value2', 'value3'], $container->getParameter('configuration.bar')); + } +}