0

I am using Symfony 3.2 with the FOSUserBundle and I am trying to write functional tests for function that require authentication in specific roles.

I used the approach posted by @tftd here, but when I run a phpunit test, I get a 500 error: There is no user provider for user "Symfony\Component\Security\Core\User\User".

My webTestCase class looks like this:

abstract class CustomWebTestCase extends WebTestCase
{

    /**
     * @param array|null $roles
     * @return \Symfony\Bundle\FrameworkBundle\Client
     *
     * https://stackoverflow.com/questions/35565196/fosuserbundle-phpunit-mock-a-user
     */
    protected static function createAuthenticatedClient(array $roles = null) {
        // Assign default user roles if no roles have been passed.
        if($roles == null) {
            $role = new Role('ROLE_SUPER_ADMIN');
            $roles = array($role);
        } else {
            $tmpRoles = array();
            foreach($roles as $role)
            {
                $role = new Role($role);
                $tmpRoles[] = $role;
            }
            $roles = $tmpRoles;
        }

        $user = new User('test_super_admin', 'passwd', $roles);

        return self::createAuthentication(static::createClient(), $user);
    }

    private static function createAuthentication(Client $client, User $user) {
        // Read below regarding config_test.yml!
        $session = $client->getContainer()->get('session');

        // Authenticate
        $firewall = 'main'; // This  MUST MATCH the name in your security.firewalls.->user_area<-
        $token = new UsernamePasswordToken($user, null, $firewall, $user->getRoles());
        $session->set('_security_'.$firewall, serialize($token));
        $session->save();

        // Save authentication
        $cookie = new Cookie($session->getName(), $session->getId());
        $client->getCookieJar()->set($cookie);

        return $client;
    }

And my test routine looks like this:

class TryoutAdminControllerTest extends CustomWebTestCase
{
    public function testTryoutListAction()
    {
        $authenticatedClient = self::createAuthenticatedClient(array('ROLE_USER'));
        $crawler = $authenticatedClient->request('GET', '/admin/tryout');
        $this->assertEquals(302, $authenticatedClient->getResponse()->getStatusCode(), 'No access allowed!');

        $authorizedClient = self::createAuthenticatedClient(array('ROLE_ADMIN'));
        $crawler = $authorizedClient->request('GET', '/admin/tryout');
        $this->assertEquals(200, $authorizedClient->getResponse()->getStatusCode(), 'Access granted!');
    }
}

security.yml:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username

    firewalls:
        main:
            pattern: ^/
            form_login:
                provider: fos_userbundle
                csrf_token_generator: security.csrf.token_manager

            logout:       true
            anonymous:    true
            switch_user:  true
            remember_me:
                secret:   '%secret%'
                lifetime: 604800 # 1 week in seconds
                path:     /
                domain:   ~
                user_provider: fos_userbundle

And finally config_test.yml

imports:
    - { resource: config_dev.yml }

framework:
    test: ~
    session:
        storage_id: session.storage.mock_file
    profiler:
        collect: false

web_profiler:
    toolbar: false
    intercept_redirects: false

swiftmailer:
    disable_delivery: true

If someone could let me know what I'm missing, I'd appreciate it!

Dan Meigs
  • 363
  • 4
  • 13
  • You shouldn't mock in a functional tests. Mocks are good for unit tests. Just use some fixtures, that load the users you need. Then you can authenticate users with a simple trick, like this one https://github.com/Bee-Lab/BeelabTestBundle/blob/master/Test/WebTestCase.php#L136 – Massimiliano Arione Aug 18 '17 at 11:53

1 Answers1

0

You probably need to instantiate your own User class that extends from FOS\UserBundle\Model\User instead of Symfony\Component\Security\Core\User\User in your WebTestCase since you are using the FosUserBundle user provider.

Consider using a test database and data fixtures as you will probably need it for your functional tests. The LiipFunctionalTestBundle may help.

Keep in mind that nothing is being mocked in this test, as suggested in the question title. Mocks must be used in unit tests, not in functional tests.

Renan Taranto
  • 562
  • 4
  • 8