diff --git a/docs/index.rst b/docs/index.rst index 26b7ec23..60612f84 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1608,35 +1608,26 @@ PHPUnit Data Providers It is possible to use factories in `PHPUnit data providers `_. -Their usage depends on which Foundry version you are running: +Their usage depends on whether you're using Foundry's `PHPUnit Extension`_ or not.: -Data Providers with Foundry ^2.2 -................................ +With PHPUnit Extension +...................... -From version 2.2, Foundry provides an extension for PHPUnit. -You can install it by modifying you ``phpunit.xml.dist``: +.. versionadded:: 2.2 -.. configuration-block:: + The ability to call ``Factory::create()`` in data providers was introduced in Foundry 2.2. - .. code-block:: xml - - - - - - - .. warning:: - This PHPUnit extension requires at least PHPUnit 11.4. + You will need at least PHPUnit 11.4 to call ``Factory::create()`` in your data providers. -Using this extension will allow to use your factories in your data providers the same way you're using them in tests. -Thanks to it, you can: +Thanks to Foundry's `PHPUnit Extension`_, you'll be able to use your factories in your data providers the same way +you're using them in tests. Thanks to it, you can: * Call ``->create()`` or ``::createOne()`` or any other method which creates objects in unit tests (using ``PHPUnit\Framework\TestCase``) and functional tests (``Symfony\Bundle\FrameworkBundle\Test\KernelTestCase``) * Use `Factories as Services`_ in functional tests * Use `faker()` normally, without wrapping its call in a callable - + :: use App\Factory\PostFactory; @@ -1654,21 +1645,22 @@ Thanks to it, you can: yield [PostWithServiceFactory::createOne()]; yield [PostFactory::createOne(['body' => faker()->sentence()]; } - - + .. warning:: Because Foundry is relying on its `Proxy mechanism `_, when using persistence, your factories must extend ``Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory`` to work in your data providers. - + .. warning:: - For the same reason, you should not call methods from `Proxy` class in your data providers, - not even ``->_real()``. + For the same reason, you should not call methods from `Proxy` class in your data providers, not even ``->_real()``. + +Without PHPUnit Extension +......................... -Data Providers before Foundry v2.2 -.................................. +Data providers are computed early in the phpunit process before Foundry is booted. +Be sure your data provider returns only instances of ``Factory`` and you do not try to call ``->create()`` on them: :: @@ -1690,11 +1682,6 @@ Data Providers before Foundry v2.2 yield [PostFactory::new()->published()]; } -.. note:: - - Be sure your data provider returns only instances of ``Factory`` and you do not try to call ``->create()`` on them. - Data providers are computed early in the phpunit process before Foundry is booted. - .. note:: For the same reason as above, it is not possible to use `Factories as Services`_ with required @@ -2168,6 +2155,39 @@ Objects can be fetched from pools in your tests, fixtures or other stories: ProvinceStory::getRandomRange('be', 1, 4); // between 1 and 4 random Province|Proxy's from "be" pool ProvinceStory::getPool('be'); // all Province|Proxy's from "be" pool +#[WithStory] Attribute +~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + + The `#[WithStory]` attribute was added in Foundry 2.3. + +.. warning:: + + The `PHPUnit Extension`_ for Foundry is needed to use ``#[WithStory]`` attribute. + +You can use the ``#[WithStory]`` attribute to load stories in your tests: + +:: + + use App\Story\CategoryStory; + use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; + use Zenstruck\Foundry\Attribute\WithStory; + + // You can use the attribute on the class... + #[WithStory(CategoryStory::class)] + final class NeedsCategoriesTest extends KernelTestCase + { + // ... or on the method + #[WithStory(CategoryStory::class)] + public function testThatNeedStories(): void + { + // ... + } + } + +If used on the class, the story will be loaded before each test method. + Static Analysis --------------- @@ -2181,6 +2201,33 @@ Please, enable it with: $ vendor/bin/psalm-plugin enable zenstruck/foundry +PHPUnit Extension +----------------- + +Foundry is shipped with an extension for PHPUnit. You can install it by modifying the file ``phpunit.xml.dist``: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + +This extension provides the following features: +- support for the `#[WithStory] Attribute`_ +- ability to use ``Factory::create()`` in `PHPUnit Data Providers`_ (along with PHPUnit ^11.4) + +.. versionadded:: 2.2 + + The PHPUnit extension was introduced in Foundry 2.2. + +.. warning:: + + The PHPUnit extension is only compatible with PHPUnit 10+. + Bundle Configuration -------------------- diff --git a/phpunit b/phpunit index c291e79a..d14150c0 100755 --- a/phpunit +++ b/phpunit @@ -43,8 +43,8 @@ fi DAMA_EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension" FOUNDRY_EXTENSION="Zenstruck\Foundry\PHPUnit\FoundryExtension" -if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ] && [ "${PHPUNIT_VERSION}" != "11" ]; then - echo "❌ USE_FOUNDRY_PHPUNIT_EXTENSION could only be used with PHPUNIT_VERSION=11"; +if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ] && [ "${PHPUNIT_VERSION}" = "9" ]; then + echo "❌ USE_FOUNDRY_PHPUNIT_EXTENSION cannot be used with PHPUNIT_VERSION=10"; exit 1; fi diff --git a/src/PHPUnit/FoundryExtension.php b/src/PHPUnit/FoundryExtension.php index 74e2ec24..20cbb5a2 100644 --- a/src/PHPUnit/FoundryExtension.php +++ b/src/PHPUnit/FoundryExtension.php @@ -24,26 +24,24 @@ */ final class FoundryExtension implements Runner\Extension\Extension { - public const MIN_PHPUNIT_VERSION = '11.4'; - public function bootstrap( TextUI\Configuration\Configuration $configuration, Runner\Extension\Facade $facade, Runner\Extension\ParameterCollection $parameters, ): void { - if (!ConstraintRequirement::from(self::MIN_PHPUNIT_VERSION)->isSatisfiedBy(Runner\Version::id())) { - throw new \LogicException(\sprintf('Your PHPUnit version (%s) is not compatible with the minimum version (%s) needed to use this extension.', Runner\Version::id(), self::MIN_PHPUNIT_VERSION)); - } - // shutdown Foundry if for some reason it has been booted before if (Configuration::isBooted()) { Configuration::shutdown(); } - $facade->registerSubscribers( - new BootFoundryOnDataProviderMethodCalled(), - new ShutdownFoundryOnDataProviderMethodFinished(), - new BuildStoryOnTestPrepared(), - ); + $subscribers = [new BuildStoryOnTestPrepared()]; + + if (ConstraintRequirement::from('11.4')->isSatisfiedBy(Runner\Version::id())) { + // those deal with data provider events which can be useful only if PHPUnit 11.4 is used + $subscribers[] = new BootFoundryOnDataProviderMethodCalled(); + $subscribers[] = new ShutdownFoundryOnDataProviderMethodFinished(); + } + + $facade->registerSubscribers(...$subscribers); } } diff --git a/tests/Fixture/Stories/ServiceStory.php b/tests/Fixture/Stories/ServiceStory.php new file mode 100644 index 00000000..943652f8 --- /dev/null +++ b/tests/Fixture/Stories/ServiceStory.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Fixture\Stories; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Routing\RouterInterface; +use Zenstruck\Foundry\Story; +use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory; + +/** + * @author Nicolas PHILIPPE + */ + final class ServiceStory extends Story +{ + public function __construct( + private readonly RouterInterface $router + ) { + } + + public function build(): void + { + $this->addState( + 'foo', + GenericEntityFactory::createOne(['prop1' => $this->router->getContext()->getHost()]) + ); + } +} diff --git a/tests/Fixture/TestKernel.php b/tests/Fixture/TestKernel.php index 63e61850..589e903e 100644 --- a/tests/Fixture/TestKernel.php +++ b/tests/Fixture/TestKernel.php @@ -28,6 +28,7 @@ use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; use Zenstruck\Foundry\Tests\Fixture\Stories\GlobalInvokableService; use Zenstruck\Foundry\Tests\Fixture\Stories\GlobalStory; +use Zenstruck\Foundry\Tests\Fixture\Stories\ServiceStory; use Zenstruck\Foundry\ZenstruckFoundryBundle; /** @@ -162,6 +163,7 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load $c->register(GlobalInvokableService::class); $c->register(ArrayFactory::class)->setAutowired(true)->setAutoconfigured(true); $c->register(Object1Factory::class)->setAutowired(true)->setAutoconfigured(true); + $c->register(ServiceStory::class)->setAutowired(true)->setAutoconfigured(true); } protected function configureRoutes(RoutingConfigurator $routes): void diff --git a/tests/Integration/Attribute/WithStory/WithStoryOnClassTest.php b/tests/Integration/Attribute/WithStory/WithStoryOnClassTest.php index a20aaa37..c72cca81 100644 --- a/tests/Integration/Attribute/WithStory/WithStoryOnClassTest.php +++ b/tests/Integration/Attribute/WithStory/WithStoryOnClassTest.php @@ -18,9 +18,9 @@ /** * @author Nicolas PHILIPPE - * @requires PHPUnit 11.4 + * @requires PHPUnit 10 */ -#[RequiresPhpunit('11.4')] +#[RequiresPhpunit('10')] #[RequiresPhpunitExtension(FoundryExtension::class)] #[WithStory(EntityStory::class)] final class WithStoryOnClassTest extends KernelTestCase diff --git a/tests/Integration/Attribute/WithStory/WithStoryOnMethodTest.php b/tests/Integration/Attribute/WithStory/WithStoryOnMethodTest.php index 18fdb5a4..b51fc532 100644 --- a/tests/Integration/Attribute/WithStory/WithStoryOnMethodTest.php +++ b/tests/Integration/Attribute/WithStory/WithStoryOnMethodTest.php @@ -14,13 +14,14 @@ use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory; use Zenstruck\Foundry\Tests\Fixture\Stories\EntityPoolStory; use Zenstruck\Foundry\Tests\Fixture\Stories\EntityStory; +use Zenstruck\Foundry\Tests\Fixture\Stories\ServiceStory; use Zenstruck\Foundry\Tests\Integration\RequiresORM; /** * @author Nicolas PHILIPPE - * @requires PHPUnit 11.4 + * @requires PHPUnit 10 */ -#[RequiresPhpunit('11.4')] +#[RequiresPhpunit('10')] #[RequiresPhpunitExtension(FoundryExtension::class)] final class WithStoryOnMethodTest extends KernelTestCase { @@ -47,4 +48,13 @@ public function can_use_multiple_story_in_attribute(): void { GenericEntityFactory::assert()->count(5); } + + /** + * @test + */ + #[WithStory(ServiceStory::class)] + public function can_use_service_story(): void + { + $this->assertSame('localhost', ServiceStory::get('foo')->getProp1()); + } } diff --git a/tests/Integration/Attribute/WithStory/WithStoryOnParentClassTest.php b/tests/Integration/Attribute/WithStory/WithStoryOnParentClassTest.php index aa865752..f5046028 100644 --- a/tests/Integration/Attribute/WithStory/WithStoryOnParentClassTest.php +++ b/tests/Integration/Attribute/WithStory/WithStoryOnParentClassTest.php @@ -13,9 +13,9 @@ /** * @author Nicolas PHILIPPE - * @requires PHPUnit 11.4 + * @requires PHPUnit 10 */ -#[RequiresPhpunit('11.4')] +#[RequiresPhpunit('10')] #[RequiresPhpunitExtension(FoundryExtension::class)] #[WithStory(EntityPoolStory::class)] final class WithStoryOnParentClassTest extends ParentClassWithStoryAttributeTestCase