Search code examples
symfonyphpunitsymfony-3.2

authorizationChecker->isGranted in a phpUnit test


I'm currently creating my unit test for my application but it's the first time I do it.

I want to test this function :

/**
 * This method search if the user is already in a team for the same tournament than the one passed in argument
 * @param User $user
 * @param Team $team
 * @return bool|Team|mixed
 */
public function isAlreadyApplicant($user, Team $team) {
    if (!$user || !$this->authorizationChecker->isGranted("ROLE_USER")) {
        return false;
    }

    foreach ($user->getApplications() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }

    foreach ($user->getTeams() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }

    foreach ($user->getManagedTeam() as $userTeam) {
        /** @var Team $userTeam */
        if ($userTeam->getTournament()->getId() === $team->getTournament()->getId()) {
            return $userTeam;
        }
    }
    return false;
}

As you can see, the first test is to check if the user have the ROLE_USER.

When I try to "log my user", I have this message :

Fatal error: Call to a member function getToken() on null in \vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authorization\AuthorizationChecker.php on line 56

I tried what I found in the Symfony doc but I must miss something. This is my test Class:

class ApplicationCheckerTest extends WebTestCase
{
    protected $client = null;

    public function setUp() {
        $this->client = static::createClient();
        /** @var User $user */
        $user = $this->client->getContainer()->get('doctrine')->getManager()->getRepository('MGDUserBundle:User')->findOneBy(array("email" => '[email protected]'));
        $this->loginUser("main", $user);
    }

    protected function loginUser($firewallName, UserInterface $user, array $options = array(), array $server = array())
    {
        $this->client = static::createClient();

        $token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles());
        static::$kernel->getContainer()->get('security.token_storage')->setToken($token);

        $session = $this->client->getContainer()->get('session');
        $session->set('_security_'.$firewallName, serialize($token));
        $session->save();
        $cookie = new Cookie($session->getName(), $session->getId());
        $this->client->getCookieJar()->set($cookie);
    }

    public function testIsAlreadyApplicantIsNotConnected() 
    {
        $user = new User();
        $team = new Team();
        $router = $this->createMock(Router::class);
        $authorizationChecker = $this->createMock(AuthorizationChecker::class);

        $applicationChecker = new ApplicationChecker($router, $authorizationChecker);
        $applicationChecker->isAlreadyApplicant($user, $team);
    }
}

And my security.yml looks like :

firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false

    main:
        pattern:    ^/
        anonymous:  true
        provider:   main
        form_login:
            login_path: fos_user_security_login
            check_path: fos_user_security_check
        logout:
            path:   fos_user_security_logout
            target: /
        remember_me:
            secret: '%secret%'

I try to connect a user I created with fixtures.

I'm not sure about the way I try to do it, maybe I'm on the wrong path, don't hesitate to correct me if I'm wrong!

For information, I'm in Symfony 3.2.13

Have a good day


Solution

  • The WebTestCase and the example from the Symfony documentation that you're using should be used for a functional test of your controller.

    The way you have set up your authentication for the client is correct. But you are not using that client to make a request to the bootstrapped Symfony kernel in your test case. You are just making a simple unit test on a manually created instance of your service, which is fine.

    You can simply use the PHPUnit\Framework\TestCase for that.

    It is possible to use the WebTestCase to test your service and overwrite the TokenStorage which is used in the AuthorizationChecker in the kernel container instead of the client container, and then also fetch your service from the kernel container instead of instantiating it yourself. But I don't see much benefit in it. There is no need to test the Symfony component (if isGranted is working). That is in the scope of the Symfony project and most likely already covered there.

    Your error

    The reason for your error is, that a final method can't be mocked. As a workaround you can set the dependency in your ApplicationChecker constructor to AuthorizationCheckerInterface and then create a mock from the interface in your test.

    $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
    $authorizationChecker->method('isGranted')->willReturn(true);