From d61193ae35cb23a656186efb723afa23982edde9 Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Wed, 18 Dec 2024 18:38:12 +0100 Subject: [PATCH] feat: throw an error if Factories trait is not used in a KernelTestCase --- .gitignore | 2 +- .php-cs-fixer.dist.php | 11 ++++ phpunit-10.xml.dist | 1 + phpunit-paratest.xml.dist | 1 + phpunit.xml.dist | 1 + src/Configuration.php | 2 + src/Exception/FactoriesTraitNotUsed.php | 58 +++++++++++++++++++ src/ObjectFactory.php | 3 + src/PHPUnit/BuildStoryOnTestPrepared.php | 3 + ...singWithStoryWithoutFactoriesTraitTest.php | 27 +++++++++ ...TestCaseWithBothTraitsInWrongOrderTest.php | 46 +++++++++++++++ .../KernelTestCaseWithBothTraitsTest.php | 46 +++++++++++++++ .../KernelTestCaseWithFactoriesTraitTest.php | 45 ++++++++++++++ ...TestCaseWithOnlyResetDatabaseTraitTest.php | 52 +++++++++++++++++ ...ernelTestCaseWithoutFactoriesTraitTest.php | 53 +++++++++++++++++ .../UnitTestCaseWithFactoriesTraitTest.php | 33 +++++++++++ .../UnitTestCaseWithoutFactoriesTraitTest.php | 31 ++++++++++ ...ng_with_story_without_factories_trait.phpt | 31 ++++++++++ tests/Integration/Maker/MakerTestCase.php | 3 + 19 files changed, 448 insertions(+), 1 deletion(-) create mode 100644 src/Exception/FactoriesTraitNotUsed.php create mode 100644 tests/Fixture/ForceFactoriesTraitUsage/KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsInWrongOrderTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithFactoriesTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithOnlyResetDatabaseTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithoutFactoriesTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithFactoriesTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithoutFactoriesTraitTest.php create mode 100644 tests/Integration/ForceFactoriesTraitUsage/kernel_test_case_using_with_story_without_factories_trait.phpt diff --git a/.gitignore b/.gitignore index 6d5aba94..58efde94 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ /.phpunit.cache /vendor/ /bin/tools/*/vendor/ -/bin/tools/php-cs-fixer/composer.lock +/bin/tools/csfixer /build/ /.php-cs-fixer.cache /.phpunit.result.cache diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 5fca00a6..7cd6e13e 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -7,6 +7,17 @@ \file_get_contents('https://raw.githubusercontent.com/zenstruck/.github/main/.php-cs-fixer.dist.php') ); +$finder = PhpCsFixer\Finder::create() + ->in([__DIR__.'/src', __DIR__.'/tests']) + + // we don't want to use @test annotations, to prevent deprecations notices, + // which would make the .phpt test fail with recent PHPUnit versions + ->notName('KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php') + + // we want to keep the traits in "wrong order" + ->notName('KernelTestCaseWithBothTraitsInWrongOrderTest.php') +; + try { return require $file; } finally { diff --git a/phpunit-10.xml.dist b/phpunit-10.xml.dist index b9758154..2ec36faf 100644 --- a/phpunit-10.xml.dist +++ b/phpunit-10.xml.dist @@ -18,6 +18,7 @@ tests tests/Integration/Migration/ResetDatabaseWithMigrationTest.php + tests/Fixture tests/Integration/Migration/ResetDatabaseWithMigrationTest.php diff --git a/phpunit-paratest.xml.dist b/phpunit-paratest.xml.dist index 387352e8..33027af9 100644 --- a/phpunit-paratest.xml.dist +++ b/phpunit-paratest.xml.dist @@ -17,6 +17,7 @@ tests tests/Integration/Migration/ResetDatabaseWithMigrationTest.php + tests/Fixture diff --git a/phpunit.xml.dist b/phpunit.xml.dist index c6b9f26c..bee2840a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,6 +20,7 @@ tests tests/Integration/Migration/ResetDatabaseWithMigrationTest.php + tests/Fixture tests/Integration/Migration/ResetDatabaseWithMigrationTest.php diff --git a/src/Configuration.php b/src/Configuration.php index 048604b4..3913013f 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -101,6 +101,8 @@ public static function isBooted(): bool /** @param \Closure():self|self $configuration */ public static function boot(\Closure|self $configuration): void { + // self::throwIfComingFromKernelTestCaseWithoutFactoriesTrait(); + self::$instance = $configuration; } diff --git a/src/Exception/FactoriesTraitNotUsed.php b/src/Exception/FactoriesTraitNotUsed.php new file mode 100644 index 00000000..33d967e2 --- /dev/null +++ b/src/Exception/FactoriesTraitNotUsed.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Exception; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Test\Factories; + +/** + * @author Nicolas PHILIPPE + */ +final class FactoriesTraitNotUsed extends \LogicException +{ + /** + * @param class-string $class + */ + public function __construct(string $class) + { + parent::__construct(\sprintf('You must use the trait "%s" in "%s" in order to use Foundry.', Factories::class, $class)); + } + + public static function throwIfComingFromKernelTestCaseWithoutFactoriesTrait(): void + { + $backTrace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS); // @phpstan-ignore ekinoBannedCode.function + + foreach ($backTrace as $trace) { + if ( + '->' === ($trace['type'] ?? null) + && isset($trace['class']) + && KernelTestCase::class !== $trace['class'] + && \is_a($trace['class'], KernelTestCase::class, allow_string: true) + && !(new \ReflectionClass($trace['class']))->hasMethod('_bootFoundry') + ) { + self::throwIfClassDoesNotHaveFactoriesTrait($trace['class']); + } + } + } + + /** + * @param class-string $class + */ + public static function throwIfClassDoesNotHaveFactoriesTrait(string $class): void + { + if (!(new \ReflectionClass($class))->hasMethod('_bootFoundry')) { + throw new self($class); + } + } +} diff --git a/src/ObjectFactory.php b/src/ObjectFactory.php index 8d69933f..0635a2a0 100644 --- a/src/ObjectFactory.php +++ b/src/ObjectFactory.php @@ -11,6 +11,7 @@ namespace Zenstruck\Foundry; +use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed; use Zenstruck\Foundry\Object\Instantiator; /** @@ -43,6 +44,8 @@ abstract public static function class(): string; */ public function create(callable|array $attributes = []): object { + FactoriesTraitNotUsed::throwIfComingFromKernelTestCaseWithoutFactoriesTrait(); + $parameters = $this->normalizeAttributes($attributes); foreach ($this->beforeInstantiate as $hook) { diff --git a/src/PHPUnit/BuildStoryOnTestPrepared.php b/src/PHPUnit/BuildStoryOnTestPrepared.php index 8689dbfe..ff3ea9eb 100644 --- a/src/PHPUnit/BuildStoryOnTestPrepared.php +++ b/src/PHPUnit/BuildStoryOnTestPrepared.php @@ -16,6 +16,7 @@ use PHPUnit\Event; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Zenstruck\Foundry\Attribute\WithStory; +use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed; /** * @internal @@ -46,6 +47,8 @@ public function notify(Event\Test\Prepared $event): void throw new \InvalidArgumentException(\sprintf('The test class "%s" must extend "%s" to use the "%s" attribute.', $test->className(), KernelTestCase::class, WithStory::class)); } + FactoriesTraitNotUsed::throwIfClassDoesNotHaveFactoriesTrait($test->className()); + foreach ($withStoryAttributes as $withStoryAttribute) { $withStoryAttribute->newInstance()->story::load(); } diff --git a/tests/Fixture/ForceFactoriesTraitUsage/KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php b/tests/Fixture/ForceFactoriesTraitUsage/KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php new file mode 100644 index 00000000..9443c97a --- /dev/null +++ b/tests/Fixture/ForceFactoriesTraitUsage/KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixture\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Attribute\WithStory; +use Zenstruck\Foundry\Tests\Fixture\Stories\ObjectStory; + +#[WithStory(ObjectStory::class)] +final class KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest extends KernelTestCase +{ + public function test_using_story_with_attribute_without_factories_trait_should_throw(): void + { + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsInWrongOrderTest.php b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsInWrongOrderTest.php new file mode 100644 index 00000000..6ac322d8 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsInWrongOrderTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Test\Factories; +use Zenstruck\Foundry\Test\ResetDatabase; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class KernelTestCaseWithBothTraitsInWrongOrderTest extends KernelTestCase +{ + use ResetDatabase, Factories; + + /** + * @test + */ + public function should_not_throw(): void + { + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } + + /** + * @test + */ + public function should_not_throw_even_when_kernel_is_booted(): void + { + self::getContainer(); + + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsTest.php b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsTest.php new file mode 100644 index 00000000..9324d38b --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithBothTraitsTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Test\Factories; +use Zenstruck\Foundry\Test\ResetDatabase; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class KernelTestCaseWithBothTraitsTest extends KernelTestCase +{ + use Factories, ResetDatabase; + + /** + * @test + */ + public function should_not_throw(): void + { + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } + + /** + * @test + */ + public function should_not_throw_even_when_kernel_is_booted(): void + { + self::getContainer(); + + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithFactoriesTraitTest.php b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithFactoriesTraitTest.php new file mode 100644 index 00000000..9d7f90d5 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithFactoriesTraitTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Test\Factories; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class KernelTestCaseWithFactoriesTraitTest extends KernelTestCase +{ + use Factories; + + /** + * @test + */ + public function should_not_throw(): void + { + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } + + /** + * @test + */ + public function should_not_throw_even_when_kernel_is_booted(): void + { + self::getContainer(); + + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithOnlyResetDatabaseTraitTest.php b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithOnlyResetDatabaseTraitTest.php new file mode 100644 index 00000000..d07cd658 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithOnlyResetDatabaseTraitTest.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed; +use Zenstruck\Foundry\Test\ResetDatabase; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class KernelTestCaseWithOnlyResetDatabaseTraitTest extends KernelTestCase +{ + use ResetDatabase; + + /** + * @test + */ + public function not_using_foundry_should_not_throw(): void + { + $this->expectNotToPerformAssertions(); + } + + /** + * @test + */ + public function not_using_foundry_should_not_throw_even_when_container_is_used(): void + { + self::getContainer()->get('.zenstruck_foundry.configuration'); + + $this->expectNotToPerformAssertions(); + } + + /** + * @test + */ + public function using_foundry_should_throw(): void + { + $this->expectException(FactoriesTraitNotUsed::class); + + Object1Factory::createOne(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithoutFactoriesTraitTest.php b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithoutFactoriesTraitTest.php new file mode 100644 index 00000000..e65e21a8 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/KernelTestCaseWithoutFactoriesTraitTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; +use Zenstruck\Foundry\Exception\FactoriesTraitNotUsed; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class KernelTestCaseWithoutFactoriesTraitTest extends KernelTestCase +{ + /** + * @test + */ + public function using_foundry_without_trait_should_throw(): void + { + $this->expectException(FactoriesTraitNotUsed::class); + + Object1Factory::createOne(); + } + + /** + * @test + */ + public function using_foundry_without_trait_should_throw_even_when_kernel_is_booted(): void + { + $this->expectException(FactoriesTraitNotUsed::class); + + self::getContainer()->get('.zenstruck_foundry.configuration'); + + Object1Factory::createOne(); + } + + /** + * @test + */ + public function booting_kernel_without_using_foundry_and_without_factories_trait_should_not_throw(): void + { + self::getContainer()->get('.zenstruck_foundry.configuration'); + + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithFactoriesTraitTest.php b/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithFactoriesTraitTest.php new file mode 100644 index 00000000..34077b9d --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithFactoriesTraitTest.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use PHPUnit\Framework\TestCase; +use Zenstruck\Foundry\Test\Factories; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class UnitTestCaseWithFactoriesTraitTest extends TestCase +{ + use Factories; + + /** + * @test + */ + public function should_not_throw(): void + { + Object1Factory::createOne(); + + $this->expectNotToPerformAssertions(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithoutFactoriesTraitTest.php b/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithoutFactoriesTraitTest.php new file mode 100644 index 00000000..76bd41b7 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/UnitTestCaseWithoutFactoriesTraitTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Integration\ForceFactoriesTraitUsage; + +use PHPUnit\Framework\TestCase; +use Zenstruck\Foundry\Exception\FoundryNotBooted; +use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; + +final class UnitTestCaseWithoutFactoriesTraitTest extends TestCase +{ + /** + * @test + */ + public function should_throw(): void + { + $this->expectException(FoundryNotBooted::class); + + Object1Factory::createOne(); + } +} diff --git a/tests/Integration/ForceFactoriesTraitUsage/kernel_test_case_using_with_story_without_factories_trait.phpt b/tests/Integration/ForceFactoriesTraitUsage/kernel_test_case_using_with_story_without_factories_trait.phpt new file mode 100644 index 00000000..d8fd5be5 --- /dev/null +++ b/tests/Integration/ForceFactoriesTraitUsage/kernel_test_case_using_with_story_without_factories_trait.phpt @@ -0,0 +1,31 @@ +--TEST-- +phpunit --version +--FILE-- +run($_SERVER['argv']); +?> +--EXPECTF-- +PHPUnit %s by Sebastian Bergmann and contributors. + +Runtime: PHP %s + +. 1 / 1 (100%) + +Time: %s, Memory: %s MB + +There was 1 PHPUnit test runner warning: + +1) Exception in third-party event subscriber: You must use the trait "Zenstruck\Foundry\Test\Factories" in "Fixture\ForceFactoriesTraitUsage\KernelTestCaseUsingWithStoryWithoutFactoriesTraitTest" in order to use Foundry. +%A + +WARNINGS! +Tests: 1, Assertions: 0, Warnings: 1, PHPUnit Deprecations: 1. diff --git a/tests/Integration/Maker/MakerTestCase.php b/tests/Integration/Maker/MakerTestCase.php index 1e830d3d..2984dd64 100644 --- a/tests/Integration/Maker/MakerTestCase.php +++ b/tests/Integration/Maker/MakerTestCase.php @@ -14,6 +14,7 @@ use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\String\Slugger\AsciiSlugger; +use Zenstruck\Foundry\Test\Factories; /** * @author Kevin Bond @@ -21,6 +22,8 @@ */ abstract class MakerTestCase extends KernelTestCase { + use Factories; + /** * @before */