phpunitsymfony5

Symfony: How to inject AuthorizationChecker in phpunit Test


A colleage changed the code of my service to use the isGranted method of of the AuthorizationCheckerInterface to check the rights. Like this:

$this->authorizationChecker->isGranted(Roles::ROLE_PERSON_VIEW);

Regretfully, he didn't adapt my phpunit tests so I am left with changing my tests so they pass again.

The service I am testing is created like this in my test:

protected function getService(string $id): ?object
{
    $kernel = static::createKernel();
    $kernel->boot();

    return $kernel
        ->getContainer()
        ->get($id);
}

    /** @var AuthorizationCheckerInterface $authorizationChecker */
    $authorizationChecker = $this->getService('AuthorizationChecker');

    $this->manager = new Manager(
        $entityManager,
        $fooService,
        $baaService,
        $authorizationChecker
    );

Which results in the error:

ServiceNotFoundException: You have requested a non-existent service "AuthorizationChecker"

I first tried mocking the service, but without defining the result I got "null" and defining the result makes it quite useless for my tests, because I mock the user right already and want to test if they play together correctly with the rest of my code, so it is a kind of functional test, rather than a unit test.

If I create the AuthorizationChecker manually I have the same problem with the TokenStorageInterface and the AccessDecisionManagerInterface.

What is the best way to test my code?


Solution

  • What I did was creating a variation of mocks with closures. I really hoped I could avoid that. So it was a kind of middle way between really using the AuthorizationChecker and writing it again as a mock. However if anyone has a more satisfying solution that would be great.

    My problem is that with every method I test I check different rights/roles, so just setting the mock to give back true or falseisn't enough. What I have here is that I can set it up for two different Symfony roles in one go and there for I have 4 variations. 'FACH' and 'PERSON' are substrings of my role names. The whole role name would be something like PRESON_CREATE

    This is what I did:

    
        private function mockAuthorizationChecker(string $role, bool $person = false, bool $fach = false)
        {
            $this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
            if ($person && $fach) {
                $this->authorizationChecker
                    ->method('isGranted')
                    ->willReturnCallback(
                        function ($role) {
                            $isGranted = false;
                            if (strpos($role, 'FACH')) {
                                $isGranted = true;
                            }
                            if (strpos($role, 'PERSON')) {
                                $isGranted = true;
                            }
    
                            return $isGranted;
                        }
                    );
            }
    
            if ($person && !$fach) {
                $this->authorizationChecker
                    ->method('isGranted')
                    ->willReturnCallback(
                        function ($role) {
                            $isGranted = false;
                            if (strpos($role, 'FACH')) {
                                $isGranted = false;
                            }
                            if (strpos($role, 'PERSON')) {
                                $isGranted = true;
                            }
    
                            return $isGranted;
                        }
                    );
            }
    
            if (!$person && $fach) {
                $this->authorizationChecker
                    ->method('isGranted')
                    ->willReturnCallback(
                        function ($role) {
                            $isGranted = false;
                            if (strpos($role, 'FACH')) {
                                $isGranted = true;
                            }
                            if (strpos($role, 'PERSON')) {
                                $isGranted = false;
                            }
    
                            return $isGranted;
                        }
                    );
            }
    
            if (!$person && !$fach) {
                $this->authorizationChecker
                    ->method('isGranted')
                    ->willReturnCallback(
                        function ($role) {
                            $isGranted = false;
                            if (strpos($role, 'FACH')) {
                                $isGranted = false;
                            }
                            if (strpos($role, 'PERSON')) {
                                $isGranted = false;
                            }
    
                            return $isGranted;
                        }
                    );
            }
        }