如何在Symfony 5.3 phpunit测试中访问私有服务?

5
我想为我的Symfony 5.3应用程序中的phpunit测试定义一个功能测试用例,该用例需要容器中的私有服务security.password_hasher
我收到以下异常:
  1. App\Tests\Functional\SiteResourceTest::testCreateSite Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: 当容器被编译时,security.password_hasher服务或别名已被删除或内联。您应该使其公开,或停止直接使用容器并改用依赖注入。
我按照文档中关于在测试中检索服务的说明操作。
我做错了什么? 我怎样才能修复这个问题?
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);
    }
}

1
从您发布的链接中:如果您需要测试已删除的私有服务(那些未被任何其他服务使用的服务),则需要在config/services_test.yaml文件中将这些私有服务声明为公共服务。 - Cerad
4个回答

4

为了避免在缓存编译期间出现以下错误,我不得不重写服务的完整定义:

"security.user_password_hasher" 的定义中没有类。如果您打算在运行时动态注入此服务,请将其标记为 synthetic=true。如果这是仅由子定义使用的抽象定义,请添加 abstract=true,否则指定一个类来摆脱此错误。

services.yaml

security.user_password_hasher:
    class: Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher
    public: true
    arguments:
      [ '@security.password_hasher_factory' ]

2

我通过在services_test.yaml中显式公开服务来解决了这个问题:

services:
    Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher:
        public: true

然后通过类名检索服务

$this->passwordHasher = $container->get(UserPasswordHasher::class);

1
请注意,更改服务 ID 实际上会有两个哈希器实例浮动。在这种情况下可能无关紧要,但我认为最好在此处使用蛇形命名法。或许有点跑题了,但直接访问密码哈希器有点不寻常。通常您会访问 UserPasswordHasher,然后根据用户类型访问密码哈希器。 - Cerad

1

对于Symfony 6

如果你继承自Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

你可以通过以下方式获取容器并获取UserPasswordHasherInterface:

$paswordHasher = static::getContainer()->get(UserPasswordHasherInterface::class);

此外,你还可以获取实体管理器或存储库:

$entityManager = static::getContainer()->get('doctrine')->getManager();

$repository = static::getContainer()->get('doctrine')->getManager()->getRepository(Foo::class);

0

测试内核还可以执行以下操作:

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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接