From bba910ce6f66b89cfd0386df8ea16749c263140a Mon Sep 17 00:00:00 2001 From: Jano Paetzold Date: Fri, 31 Jul 2026 09:14:57 +0200 Subject: [PATCH] Update test stack: migrate to Foundry 2 and PHPUnit 11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foundry 1 cannot run on Symfony 8, so it must be replaced before the final bump; on the interim Symfony 7 this can happen in isolation. Foundry 2 removed the ModelFactory base class — rewrite the four test factories onto PersistentProxyObjectFactory: getDefaults() → defaults(), getClass() → class() (public static), initialize() return type self → static. Because Foundry 2 requires PHPUnit >= 9.5 (and Foundry 1 does not run on PHPUnit 11), the PHPUnit 8.5 → 11 bump and the phpunit.xml schema update ride along in the same package. Two additional fixes needed to make the suite green on PHPUnit 11: - buildForm() and configureOptions(): add missing `: void` return type to the five form types that declared them without one (EmailAddressType, HoneypotType, StartRegistration\Type, EditRegistration\Type, DeleteRegistration\Type). mapDataToForms() and mapFormsToData() were already typed correctly; Command::execute(), the FlashBag→RequestStack replacement, and the routing 'attribute' switch were already in place from Package E. - DetermineAppsSubclassHelper: skip eval()'d PHPUnit test-double classes when scanning get_declared_classes() for a RecipientInterface subclass. PHPUnit 11 auto-generates named stub classes (via eval()) for interface return types — these were picked up before App\Recipient, causing RecipientFactoryTest to fail in the full suite. PHP reports a synthetic filename for eval()'d code rather than false, so the guard uses is_file() to confirm a real file exists on disk. --- composer.json | 4 ++-- phpunit.xml.dist | 14 +++++--------- src/DeleteRegistration/Type.php | 4 ++-- src/EditRegistration/Type.php | 2 +- src/Entity/DetermineAppsSubclassHelper.php | 2 ++ src/StartRegistration/EmailAddressType.php | 4 ++-- src/StartRegistration/HoneypotType.php | 2 +- src/StartRegistration/Type.php | 2 +- tests/Factory/BlockedEmailAddressHashFactory.php | 10 +++++----- tests/Factory/NewsletterFactory.php | 10 +++++----- tests/Factory/PendingOptInFactory.php | 10 +++++----- tests/Factory/RecipientFactory.php | 10 +++++----- 12 files changed, 36 insertions(+), 38 deletions(-) diff --git a/composer.json b/composer.json index d05aed2..7db6872 100644 --- a/composer.json +++ b/composer.json @@ -30,13 +30,13 @@ "require-dev": { "doctrine/common": "^2.0 | ^3.0", "doctrine/doctrine-bundle": "^2.4", - "phpunit/phpunit": "^8.5.0", + "phpunit/phpunit": "^11.0", "symfony/browser-kit": "^7.0", "symfony/css-selector": "^7.0", "symfony/framework-bundle": "^7.0", "symfony/twig-bundle": "^7.0", "symfony/yaml": "^7.0", - "zenstruck/foundry": "^1.0" + "zenstruck/foundry": "^2.8" }, "autoload": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 78146e5..b7d4106 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ @@ -13,15 +13,11 @@ - - - + + src/ - - vendor - - - + + diff --git a/src/DeleteRegistration/Type.php b/src/DeleteRegistration/Type.php index 9fde0c9..b0495a6 100644 --- a/src/DeleteRegistration/Type.php +++ b/src/DeleteRegistration/Type.php @@ -17,7 +17,7 @@ public function __construct(UrlGeneratorInterface $urlGenerator) $this->urlGenerator = $urlGenerator; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { parent::buildForm($builder, $options); @@ -31,7 +31,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ); } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { parent::configureOptions($resolver); diff --git a/src/EditRegistration/Type.php b/src/EditRegistration/Type.php index 8b72054..3600b15 100644 --- a/src/EditRegistration/Type.php +++ b/src/EditRegistration/Type.php @@ -18,7 +18,7 @@ public function __construct(NewsletterRepositoryInterface $newsletterRepository) $this->newsletterRepository = $newsletterRepository; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $this->addNewslettersElementToForm($builder, false); diff --git a/src/Entity/DetermineAppsSubclassHelper.php b/src/Entity/DetermineAppsSubclassHelper.php index 1c8c660..0b4a84f 100644 --- a/src/Entity/DetermineAppsSubclassHelper.php +++ b/src/Entity/DetermineAppsSubclassHelper.php @@ -3,6 +3,7 @@ namespace Webfactory\NewsletterRegistrationBundle\Entity; use Exception; +use ReflectionClass; class DetermineAppsSubclassHelper { @@ -12,6 +13,7 @@ public static function getAppsSubclassOf(string $parentClass, Exception $excepti if ( is_subclass_of($class, $parentClass) && 0 !== strpos($class, 'Webfactory\NewsletterRegistrationBundle') + && is_file((string) (new ReflectionClass($class))->getFileName()) ) { return $class; } diff --git a/src/StartRegistration/EmailAddressType.php b/src/StartRegistration/EmailAddressType.php index 372eaed..b9fa969 100644 --- a/src/StartRegistration/EmailAddressType.php +++ b/src/StartRegistration/EmailAddressType.php @@ -50,7 +50,7 @@ public function __construct( $this->translator = $translator; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->setDataMapper($this); @@ -72,7 +72,7 @@ public function getParent(): string return TextType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ 'empty_data' => null, diff --git a/src/StartRegistration/HoneypotType.php b/src/StartRegistration/HoneypotType.php index f31565e..be27b58 100644 --- a/src/StartRegistration/HoneypotType.php +++ b/src/StartRegistration/HoneypotType.php @@ -80,7 +80,7 @@ public function getParent(): string return TextType::class; } - public function configureOptions(OptionsResolver $resolver) + public function configureOptions(OptionsResolver $resolver): void { parent::configureOptions($resolver); $resolver->setDefaults([ diff --git a/src/StartRegistration/Type.php b/src/StartRegistration/Type.php index 455f9a5..04d612a 100644 --- a/src/StartRegistration/Type.php +++ b/src/StartRegistration/Type.php @@ -26,7 +26,7 @@ public function __construct(NewsletterRepositoryInterface $newsletterRepository, $this->pendingOptInFactory = $pendingOptInFactory; } - public function buildForm(FormBuilderInterface $builder, array $options) + public function buildForm(FormBuilderInterface $builder, array $options): void { $builder->add(static::ELEMENT_EMAIL_ADDRESS, EmailAddressType::class); diff --git a/tests/Factory/BlockedEmailAddressHashFactory.php b/tests/Factory/BlockedEmailAddressHashFactory.php index 66f2266..dcfb0a5 100644 --- a/tests/Factory/BlockedEmailAddressHashFactory.php +++ b/tests/Factory/BlockedEmailAddressHashFactory.php @@ -4,11 +4,11 @@ use DateTimeImmutable; use Webfactory\NewsletterRegistrationBundle\Entity\BlockedEmailAddressHash; -use Zenstruck\Foundry\ModelFactory; +use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; -final class BlockedEmailAddressHashFactory extends ModelFactory +final class BlockedEmailAddressHashFactory extends PersistentProxyObjectFactory { - protected function getDefaults(): array + protected function defaults(): array { return [ 'hash' => self::faker()->sha1(), @@ -16,14 +16,14 @@ protected function getDefaults(): array ]; } - protected function initialize(): self + protected function initialize(): static { return $this->instantiateWith(function (array $attributes): BlockedEmailAddressHash { return new BlockedEmailAddressHash($attributes['hash'], $attributes['blockDate']); }); } - protected static function getClass(): string + public static function class(): string { return BlockedEmailAddressHash::class; } diff --git a/tests/Factory/NewsletterFactory.php b/tests/Factory/NewsletterFactory.php index 7bd1ef6..8f57c7c 100644 --- a/tests/Factory/NewsletterFactory.php +++ b/tests/Factory/NewsletterFactory.php @@ -3,11 +3,11 @@ namespace Webfactory\NewsletterRegistrationBundle\Tests\Factory; use Webfactory\NewsletterRegistrationBundle\Tests\Entity\Dummy\Newsletter; -use Zenstruck\Foundry\ModelFactory; +use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; -final class NewsletterFactory extends ModelFactory +final class NewsletterFactory extends PersistentProxyObjectFactory { - protected function getDefaults(): array + protected function defaults(): array { return [ 'name' => self::faker()->word(), @@ -16,14 +16,14 @@ protected function getDefaults(): array ]; } - protected function initialize(): self + protected function initialize(): static { return $this->instantiateWith(function (array $attributes): Newsletter { return new Newsletter(null, $attributes['name'], $attributes['rank'], $attributes['visible']); }); } - protected static function getClass(): string + public static function class(): string { return Newsletter::class; } diff --git a/tests/Factory/PendingOptInFactory.php b/tests/Factory/PendingOptInFactory.php index c529f71..0d8e01f 100644 --- a/tests/Factory/PendingOptInFactory.php +++ b/tests/Factory/PendingOptInFactory.php @@ -5,11 +5,11 @@ use DateTimeImmutable; use Webfactory\NewsletterRegistrationBundle\Entity\EmailAddress; use Webfactory\NewsletterRegistrationBundle\Tests\Entity\Dummy\PendingOptIn; -use Zenstruck\Foundry\ModelFactory; +use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; -final class PendingOptInFactory extends ModelFactory +final class PendingOptInFactory extends PersistentProxyObjectFactory { - protected function getDefaults(): array + protected function defaults(): array { return [ 'uuid' => self::faker()->uuid(), @@ -18,7 +18,7 @@ protected function getDefaults(): array ]; } - protected function initialize(): self + protected function initialize(): static { return $this->instantiateWith(function (array $attributes): PendingOptIn { return new PendingOptIn( @@ -30,7 +30,7 @@ protected function initialize(): self }); } - protected static function getClass(): string + public static function class(): string { return PendingOptIn::class; } diff --git a/tests/Factory/RecipientFactory.php b/tests/Factory/RecipientFactory.php index 3b71f65..059d548 100644 --- a/tests/Factory/RecipientFactory.php +++ b/tests/Factory/RecipientFactory.php @@ -4,11 +4,11 @@ use Webfactory\NewsletterRegistrationBundle\Entity\EmailAddress; use Webfactory\NewsletterRegistrationBundle\Tests\Entity\Dummy\Recipient; -use Zenstruck\Foundry\ModelFactory; +use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory; -final class RecipientFactory extends ModelFactory +final class RecipientFactory extends PersistentProxyObjectFactory { - protected function getDefaults(): array + protected function defaults(): array { return [ 'uuid' => self::faker()->uuid(), @@ -16,14 +16,14 @@ protected function getDefaults(): array ]; } - protected function initialize(): self + protected function initialize(): static { return $this->instantiateWith(function (array $attributes): Recipient { return new Recipient($attributes['uuid'], $attributes['emailAddress']); }); } - protected static function getClass(): string + public static function class(): string { return Recipient::class; }