Symfony PHPUnit tests don't authenticate user

1.1k views Asked by At

I have a Symfony 4.4 project. Login, authentication, and authorization work fine on web.

To simulate authentication in unit tests, I'm using Symfony's example.

...but that's not working. The user is not authenticated in the unit test. I get an Unauthorized with error message "Full authentication is required to access this resource."

My test:


namespace App\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;

class DefaultControllerTest extends WebTestCase
{
    private $client = null;

    public function setUp()
    {
        $this->client = static::createClient();
    }

    public function testJsonUserEndpoint()
    {
        $this->logIn();
        $crawler = $this->client->request('GET', '/json/user');

        $this->assertNotContains('invalid_credentials', $this->client->getResponse()->getContent());
    }

    private function logIn()
    {
        $user = self::$container->get(EntityManagerInterface::class)->getRepository(User::class)->findOneByMail('[email protected]');

        $session = $this->client->getContainer()->get('session');

        $firewallName = 'main';
        $firewallContext = 'main';

        $token = new PostAuthenticationGuardToken($user, $firewallName, ['ROLE_USER']);
        $session->set('_security_'.$firewallContext, serialize($token));
        $session->save();

        $cookie = new Cookie($session->getName(), $session->getId());
        $this->client->getCookieJar()->set($cookie);
    }
}

My firewall:

        main:
            anonymous:
                secret: '%env(APP_SECRET)%'
            remember_me:
                always_remember_me: true
                secret: '%env(APP_SECRET)%'
            provider: session_user_provider
            guard:
                authenticators:
                    - App\Security\Authenticator\LoginAuthenticator
                    - App\Security\Authenticator\MobileBridgeAuthenticator
                    - App\Security\Authenticator\BearerTokenAuthenticator
                    - App\Security\Authenticator\HashAuthenticator
                    - App\Security\Authenticator\ClientAuthenticator
                entry_point: App\Security\Authenticator\LoginAuthenticator

            logout:
                path: execute_logout
                target: render_login

Unit test failure:

1) App\Tests\Controller\DefaultControllerTest::testJsonUserEndpoint
Failed asserting that '{"status":"invalid_credentials","errors":["Invalid Credentials"],"debug":"Full authentication is required to access this resource.","error":"Unauthorized"}' does not contain "invalid_credentials".

How can I authenticate a user in my unit tests without using the login form?

1

There are 1 answers

0
amacrobert On

The problem was that I don't store roles in the database with the user -- they're added in my authenticators -- and passing the roles to PostAuthenticationGuardToken wasn't enough. I also had to add them to the user in the token:

$user->setRoles(['ROLE_USER']);
$token = new PostAuthenticationGuardToken($session_user, $firewallName, ['ROLE_USER']);

Actually, it didn't even matter what's in the Token's roles as long as it's an array:

$user->setRoles(['ROLE_USER']);
$token = new PostAuthenticationGuardToken($session_user, $firewallName, []);