Quiero definir un caso de prueba funcional para mis pruebas de phpunit para mi aplicación Symfony 5.3 que requiere el servicio privado security.password_hasher del contenedor.
Obtengo la siguiente excepción
App\Tests\Functional\SiteResourceTest::testCreateSiteSymfony\Component\DependencyInjection\Exception\ServiceNotFoundException : El servicio o aliassecurity.password_hasherse eliminó o se incorporó cuando se compiló el contenedor. Debe hacerlo público o dejar de usar el contenedor directamente y usar la inyección de dependencia en su lugar.
Seguí las instrucciones de la documentación sobre la recuperación de servicios en la prueba.
¿Qué estoy haciendo mal? ¿Cómo puedo arreglar esto?
class CustomApiTestCase extends ApiTestCase { protected UserPasswordHasher $passwordHasher; protected function setUp(): void { // (1) boot the Symfony kernel self::bootKernel(); // (2) use static::getContainer() to access the service container $container = static::getContainer(); // (3) run some service & test the result $this->passwordHasher = $container->get('security.password_hasher'); } protected function createUser( string $email, string $password, ): User { $user = new User(); $user->setEmail($email); $encoded = $this->passwordHasher->hash($password); $user->setPassword($encoded); $em = self::getContainer()->get('doctrine')->getManager(); $em->persist($user); $em->flush(); return $user; } protected function createUserAndLogIn(Client $client, string $email, string $password): User { $user = $this->createUser($email, $password); $this->logIn($client, $email, $password); return $user; } protected function logIn(Client $client, string $email, string $password) { $client->request('POST', '/login', [ 'headers' => ['Content-Type' => 'application/json'], 'json' => [ 'email' => $email, 'password' => $password ], ]); $this->assertResponseStatusCodeSame(204); } }Lo resolví haciendo que el servicio sea explícitamente público en services_test.yaml :
services: Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher: public: trueY luego recuperar el servicio por su nombre de clase
$this->passwordHasher = $container->get(UserPasswordHasher::class);Tuve que reescribir la definición completa del servicio para evitar el siguiente error durante la compilación del caché:
La definición de "security.user_password_hasher" no tiene clase. Si tiene la intención de inyectar este servicio dinámicamente en tiempo de ejecución, márquelo como synthetic=true. Si esta es una definición abstracta utilizada únicamente por definiciones secundarias, agregue abstract=true; de lo contrario, especifique una clase para eliminar este error.
servicios.yaml
security.user_password_hasher: class: Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher public: true arguments: [ '@security.password_hasher_factory' ]Un kernel de prueba también puede hacer lo siguiente:
class PublicService implements CompilerPassInterface { public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { if (stripos($id, 'whatEverIWant') === 0) { $definition->setPublic(true); } } foreach ($container->getAliases() as $id => $definition) { if (stripos($id, 'whatEverIWant') === 0) { $definition->setPublic(true); } } } } class AppKernel extends BaseKernel { public function build(ContainerBuilder $container) { $container->addCompilerPass(new PublicService(), PassConfig::TYPE_OPTIMIZE); parent::build($container); } }