Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 login FOS UserBundle in functional tests

How to login inside tests to be able to do user-specific actions?

like image 819
VitalyP Avatar asked Feb 07 '12 19:02

VitalyP


3 Answers

Actually, the authentication must be done like a real user do it. In that case, you have to know the plain password and fill the login form

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class XXX extends WebTestCase {
  public function testUserLogin() {

   $client = static::createClient();
   $crawler = $client->request('GET', '/login');

   $form = $crawler->selectButton('_submit')->form(array(
     '_username'  => 'user',
     '_password'  => 'pa$$word',
   ));

   $client->submit($form);
   $crawler = $client->followRedirect(); // "/" page

   // if credentials were correct, you should be logged in and ready to test your app
  }
}
like image 156
Utopik Avatar answered Nov 16 '22 02:11

Utopik


The existing answers to this question have been helpful, but none of them solved my problem directly. I'm using Symfony 2.3.

Here is my solution:

<?php

namespace CDE\TestBundle\Base;

use FOS\UserBundle\Model\User;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\BrowserKit\CookieJar;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

class BaseUserTest extends WebTestCase {

    protected $client;
    protected $container;
    protected $storage;
    protected $session;
    protected $user;
    protected $cookieJar;
    protected $cookie;
    protected $token;

    public function __construct() {
        $this->client = static::createClient();
        $this->container = $this->client->getContainer();
        $this->storage = new MockFileSessionStorage(__dir__.'/../../../../app/cache/test/sessions');
        $this->session = new Session($this->storage);

    }

    public function getUserManager() {
        return $this->container->get('cde_user.manager.user');

    }

    public function getSecurityManager() {
        return $this->container->get('fos_user.security.login_manager');

    }

    public function getUser($role = null) {
        if (!isset($this->user)) {
            $user = $this->getUserManager()->loadByUsername('user');

            if (isset($user)) {
                $this->user = $user;
            } else {
                $this->user = $this->getUserManager()->create();

                $this->user->setEnabled(true);
                $this->user->setUsername('user');
                $this->user->setEmail('[email protected]');
                $this->user->setPlainPassword('user');
                $this->getUserManager()->updatePassword($this->user);
                if (isset($role)) {
                    $this->user->addRole($role);
                }
                $this->getUserManager()->add($this->user);
            }

        }

        return $this->user;
    }

    public function logIn(User $user, Response $response) {
        $this->session->start();

        $this->cookie = new Cookie('MOCKSESSID', $this->storage->getId());
        $this->cookieJar = new CookieJar();
        $this->cookieJar->set($this->cookie);
        $this->token = new UsernamePasswordToken($user, 'user', 'main', $user->getRoles());
        $this->session->set('_security_main', serialize($this->token));


        $this->getSecurityManager()->loginUser(
            $this->container->getParameter('fos_user.firewall_name'),
            $user,
            $response
        );

        $this->session->save();
    }

    public function removeUser(User $user) {

    }
}

RestControllerTest.php

<?php

namespace CDE\ContentBundle\Tests\Controller;

use CDE\TestBundle\Base\BaseUserTest;
use Symfony\Component\HttpFoundation\Response;

class RestControllerTest extends BaseUserTest
{
    protected $comment;

    public function __construct() {
        parent::__construct();
        $this->logIn($this->getUser('ROLE_ADMIN'), new Response());

    }

    public function getGalleryManager() {
        return $this->container->get('cde_content.manager.gallery');
    }

    public function getAWSManager() {
        return $this->container->get('cde_utility.manager.aws');
    }

    public function createGallery()
    {
        //        Copy test.jpeg into the web folder
        $filename = 'gallery/user-test.jpg';
        copy(__DIR__.'/../Mock/test.jpeg', __DIR__.'/../../../../../web/'.$filename);
        $this->getAWSManager()->copyGalleryFile($filename);


        $gallery = $this->getGalleryManager()->create();
        $gallery->setUser($this->getUser());
        $gallery->setFilename($filename);
        $gallery->setTitle('test gallery');
        $gallery->setDescription('test gallery description');
        $gallery->setMarked(false);
        $this->getGalleryManager()->add($gallery);
        $this->assertEquals($gallery->getMarked(), false);
    }

    public function createComment()
    {
        $galleries = $this->getGalleryManager()->findByUser($this->getUser());
        $gallery = $galleries[0];

        $client = static::createClient();
        $client->getCookieJar()->set($this->cookie);
//        $client = static::createClient(array(), new History(), $cookieJar);

        $crawler = $client->request('POST', 'api/createComment/'.$gallery->getId(), array(
            'comment' => 'testing testing 123',
            'marked' => 'false'
        ));

        $response = $client->getResponse();
        $this->comment = json_decode($response->getContent());
        $this->assertEquals($this->comment->comment, 'testing testing 123');
        $this->assertFalse($this->comment->marked);
        $this->assertEquals($response->getStatusCode(), 200);
    }

    public function getComment()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', 'api/getComment/'.$this->comment->id);

        $response = $client->getResponse();
        $comment = json_decode($response->getContent());
        $this->assertEquals($comment->comment, 'testing testing 123');
        $this->assertFalse($comment->marked);
        $this->assertEquals($response->getStatusCode(), 200);

    }

    public function updateComment()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', 'api/updateComment');
    }

    public function deleteComment()
    {
        $client = static::createClient();

        $crawler = $client->request('DELETE', 'api/deleteComment');
    }

    public function getComments()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', 'api/getComments');
    }

    public function getGalleries()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', 'api/getGalleries');
    }

    public function removeGallery() {
        $galleries = $this->getGalleryManager()->findByUser($this->getUser());
        foreach ($galleries as $gallery) {
            $this->getGalleryManager()->remove($gallery);
        }

    }

    public function testComments() {
        $this->createGallery();
        $this->createComment();
        $this->getComment();
        $this->updateComment();
        $this->deleteComment();
        $this->getComments();
        $this->getGalleries();
        $this->removeGallery();
    }

}

The code includes a base test class (BaseUserTest.php) that can be extended to easily log in users.

I've also included an example of how to use the base class in an example test (RestControllerTest.php). Notice this code block in RestControllerTest.php:

$client = static::createClient();
$client->getCookieJar()->set($this->cookie);

The idea behind BaseUserTest is that it can create it's own session, populate the session with the user, and then save the session to the file system using MockFileSessionStorage.

The tests themselves must then set the cookie on the client.

like image 23
Chris Esplin Avatar answered Nov 16 '22 01:11

Chris Esplin


I know it is too late.. But in case someone needs this, I managed to logg a user in my unit test like this:

1- First Define a class which will give you access to services:

<?php
namespace Tests;

class ContainerAwareUnitTestCase extends \PHPUnit_Framework_TestCase
{
    protected static $kernel;
    protected static $container;

    public static function setUpBeforeClass()
    {
        self::$kernel = new \AppKernel('dev', true);
        self::$kernel->boot();

        self::$container = self::$kernel->getContainer();
    }

    public function get($serviceId)
    {
        return self::$kernel->getContainer()->get($serviceId);
    }
}

2- Then declare a test as follow:

<?php

namespace Tests\Menu;

use Tests\ContainerAwareUnitTestCase;
use UserBundle\Entity\User;
use FOS\UserBundle\Security\UserProvider;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Request;
use Menu\MenuBuilder;

class MenuBuilderTest extends ContainerAwareUnitTestCase
{
    public function provider()
    {
        return array(array('/'));

    }

    /**
     * @dataProvider provider
     */
    public function testNoAuth($uri) 
    {
        /* @var $securityContext SecurityContext */
        $securityContext = $this->get('security.context');
        $userProvider = $this->get('fos_user.user_provider.username');
        $user = $userProvider->loadUserByUsername('alex');
        $token = new UsernamePasswordToken($user, null, 'main', array('ROLE_USER'));
        $securityContext->setToken($token);

        /* @var $menuBuilder MenuBuilder */
        $menuBuilder = $this->get('event_flow_analyser.menu_builder');

        $this->assertNotNull($menuBuilder);

        $request = new Request();
        $request->attributes->set('projectName', 'ucs');

        $menu = $menuBuilder->createMainMenu($request);

        $this->assertNotNull($menu);

        $this->assertNotNull($menu->getChild('Home'));
        $this->assertNotNull($menu->getChild('Profile'));

        $this->assertEquals(null, $menu->getChild('Projects'));
    }

}    

This a complete exemple to test menuBuilder creation, but although a little out of scope, that extract should answer your need:

        $securityContext = $this->get('security.context');
        $userProvider = $this->get('fos_user.user_provider.username');
        $user = $userProvider->loadUserByUsername('alex');
        $token = new UsernamePasswordToken($user, null, 'main', array('ROLE_USER'));
        $securityContext->setToken($token);
like image 39
Alexandre Mélard Avatar answered Nov 16 '22 01:11

Alexandre Mélard