PHPUnit中Zenstruck Foundry Factory密码未哈希问题求助
在PHPUnit中使用Zenstruck Foundry时服务注入失效的问题
我在PHPUnit测试中使用Zenstruck Foundry Factory,只要涉及服务注入就完全失效。已按照文档要求启用扩展,但配置未生效,例如用户密码始终未被哈希。
PHPUnit配置
<phpunit> <extensions> <bootstrap class="Zenstruck\Foundry\PHPUnit\FoundryExtension"/> </extensions> </phpunit>
UserFactory代码
<?php namespace App\DataFixtures\Factory; use App\Entity\User; use Symfony\Component\PasswordHasher\PasswordHasherInterface; use Zenstruck\Foundry\Persistence\PersistentObjectFactory; /** * @extends PersistentObjectFactory<User> */ final class UserFactory extends PersistentObjectFactory { private static ?User $admin = null; public function __construct( private readonly ?PasswordHasherInterface $passwordHasher = null ) { parent::__construct(); } #[\Override] public static function class(): string { return User::class; } #[\Override] protected function defaults(): array|callable { return [ 'email' => self::faker()->unique()->email(), 'username' => self::faker()->userName(), 'password' => self::faker()->password(), 'color' => ColorFactory::randomOrCreate(), 'roles' => [User::ROLE_USER], ]; } #[\Override] protected function initialize(): static { return $this ->afterInstantiate(function(User $user): void { if (!$this->passwordHasher instanceof PasswordHasherInterface) { return; } $user->setPassword($this->passwordHasher->hash($user->getPassword())); }) ; } }
测试代码
#[DataProvider(methodName: 'getCollectionUrls')] public function testGetCollection(?UserFactory $userFactory, int $expectedCount, int $expectedPageCount): void { ForumsStory::load(); ForumFactory::createOne(['deletedAt' => new \DateTimeImmutable()]); if ($userFactory) { $user = $userFactory->create(); dump($user->getPassword()); // 未哈希 dump(UserFactory::createOne()->getPassword()); // 同样未哈希 $response = $this->createClientWithUserCredentials($user->getEmail())->request('GET', '/api/forums'); dump($response->toArray()); } else { $response = $this->client->request('GET', '/api/forums'); } $this->assertResponseIsSuccessful(); $this->assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8'); $this->assertJsonContains([ '@context' => '/api/contexts/Forum', '@id' => '/api/forums', '@type' => 'Collection', 'totalItems' => $expectedCount, 'view' => [ '@id' => '/api/forums?page=1', '@type' => 'PartialCollectionView', 'first' => '/api/forums?page=1', 'next' => '/api/forums?page=2', 'last' => '/api/forums?page=' . $expectedPageCount, ], ]); $this->assertCount(30, $response->toArray()['member']); $this->assertMatchesResourceCollectionJsonSchema(Forum::class); } public static function getCollectionUrls(): iterable { yield 'all anonymous forums' => [ null, 35, 2, ]; yield 'all normal user forums' => [ UserFactory::new([ 'password' => self::DEFAULT_PASSWORD, ]), 35, 2, ]; yield 'all coach forums' => [ UserFactory::new([ 'password' => self::DEFAULT_PASSWORD, 'roles' => [User::ROLE_COACH], ]), 70, 3, ]; yield 'all admin forums' => [ UserFactory::new([ 'password' => self::DEFAULT_PASSWORD, 'roles' => [User::ROLE_ADMIN], ]), 70, 3, ]; }
解决方案
1. 修正PHPUnit扩展配置
原配置使用了bootstrap标签,这是旧的加载方式,正确配置应使用extension标签:
<phpunit> <extensions> <extension class="Zenstruck\Foundry\PHPUnit\FoundryExtension"/> </extensions> </phpunit>
2. 替换正确的密码哈希服务接口
Symfony中通常使用UserPasswordHasherInterface而非底层的PasswordHasherInterface,修改UserFactory:
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; // ... public function __construct( private readonly ?UserPasswordHasherInterface $passwordHasher = null ) { parent::__construct(); } // ... ->afterInstantiate(function(User $user): void { if (!$this->passwordHasher instanceof UserPasswordHasherInterface) { return; } $user->setPassword($this->passwordHasher->hashPassword($user, $user->getPassword())); })
3. 避免在DataProvider中直接创建Factory实例
DataProvider静态执行时服务容器尚未初始化,传递配置数组而非Factory实例:
public static function getCollectionUrls(): iterable { yield 'all anonymous forums' => [ null, 35, 2, ]; yield 'all normal user forums' => [ ['password' => self::DEFAULT_PASSWORD], 35, 2, ]; yield 'all coach forums' => [ ['password' => self::DEFAULT_PASSWORD, 'roles' => [User::ROLE_COACH]], 70, 3, ]; yield 'all admin forums' => [ ['password' => self::DEFAULT_PASSWORD, 'roles' => [User::ROLE_ADMIN]], 70, 3, ]; }
修改测试方法逻辑:
#[DataProvider(methodName: 'getCollectionUrls')] public function testGetCollection(?array $userConfig, int $expectedCount, int $expectedPageCount): void { ForumsStory::load(); ForumFactory::createOne(['deletedAt' => new \DateTimeImmutable()]); if ($userConfig) { $user = UserFactory::createOne($userConfig); dump($user->getPassword()); // 此时密码已哈希 $response = $this->createClientWithUserCredentials($user->getEmail())->request('GET', '/api/forums'); dump($response->toArray()); } else { $response = $this->client->request('GET', '/api/forums'); } // ... 其余断言逻辑不变 }
4. 确保Factory被注册为服务
检查config/services_test.yaml,确保Factory被自动扫描或手动注册:
services: App\DataFixtures\Factory\UserFactory: autowire: true tags: ['zenstruck_foundry.factory']
内容的提问来源于stack exchange,提问作者Gaylord.P
相关产品推荐
相关产品推荐

