'How to access a private service in Symfony 5.3 phpunit tests?
I want to define a functional testcase for my phpunit-tests for my Symfony 5.3 application which requires the private service security.password_hasher from the container.
I get the following execption
App\Tests\Functional\SiteResourceTest::testCreateSiteSymfony\Component\DependencyInjection\Exception\ServiceNotFoundException: Thesecurity.password_hasherservice or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.
I followed the instructions from the documentation about retrieving services in the test
What am i doing wrong? How can i fix this?
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);
}
}
Solution 1:[1]
I solved it by making the service explicitly public in the services_test.yaml:
services:
Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher:
public: true
And then retrieving the service by its classname
$this->passwordHasher = $container->get(UserPasswordHasher::class);
Solution 2:[2]
I had to rewrite the complete definition of the service to prevent the following error during cache compilation:
The definition for "security.user_password_hasher" has no class. If you intend to inject this service dynamically at runtime, please mark it as synthetic=true. If this is an abstract definition solely used by child definitions, please add abstract=true, otherwise specify a class to get rid of this error.
services.yaml
security.user_password_hasher:
class: Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher
public: true
arguments:
[ '@security.password_hasher_factory' ]
Solution 3:[3]
A test kernel may also do the following:
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);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | yivi |
| Solution 2 | Adrien G |
| Solution 3 | drzraf |
