简体   繁体   中英

Behat, Symfony 2 and FOS User: how to create a login test

I'm new with Behat and I want to create a feature to validate the login form. I'm using FOS User bundle.

The easiest way to go is do something like:

When I am on "/login"
And I fill in "username" with "admin"
And I fill in "password" with "1234"
And I press "_submit"
Then I should be on "/dashboard"

expecting that the user "admin" exists in the database with the 1234 password. But I'm pretty sure there must be a way to mock the database or create a fake user for FOS. I've been googling but couldn't find anything specific.

Ideally I think it should be something like

Given there's a user "admin" with password "1234"
[rest of the test]

Any ideas? Thanks!

You could define a specific step to log in, and reuse it in your different tests. You don't even need the password, authentification is simulated through tokens.

For example :

/**
 * @Given /^I am authenticated as "([^"]*)"$/
 */
public function iAmAuthenticatedAs($username)
{
    $driver = $this->getSession()->getDriver();
    if (!$driver instanceof BrowserKitDriver) {
        throw new UnsupportedDriverActionException('This step is only supported by the BrowserKitDriver');
    }

    $client = $driver->getClient();
    $client->getCookieJar()->set(new Cookie(session_name(), true));

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

    $user = $this->kernel->getContainer()->get('fos_user.user_manager')->findUserByUsername($username);
    $providerKey = $this->kernel->getContainer()->getParameter('fos_user.firewall_name');

    $token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
    $session->set('_security_'.$providerKey, serialize($token));
    $session->save();

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

And then call this new step with :

Scenario: Displaying the blog overview
Given I am authenticated as "bar"
  And I am on "/admin/"
 Then I should see "Admin dashboard"

Useful content :

To validate the FOSUserBundle login form you could write your feature like this

Feature: Admin authentication
  In order to gain access to my admin management area
  As an admin
  I need to be able to login

  Scenario: Logging in
    Given there is an admin user "admin" with password "1234"
    And I am on "/login"
    When I fill in "Username" with "admin"
    And I fill in "Password" with "1234"
    And I press "Log in"
    Then I should see "Connected" //or anything that proves you're connected

Then you can create the step validation in your FeatureContext.php like this :

/**
 * @Given there is an admin user :username with password :password
 */
public function thereIsAnAdminUserWithPassword($username, $password)
{   // Create an Admin User
    $user = new \AppBundle\Entity\User();// inherits form FOSUserBundle\Entity\User()
    $user->setUsername($username);
    $user->setPlainPassword($password);
    $user->setEmail("example@mail.com");// required by FOSUserBundle
    $user->setEnabled(true); // add it manually otherwise you'll get "Account is disabled" error
    $user->setRoles(array('ROLE_ADMIN'));

    // Then you must store it in your database
}

The problem is that FeatureContext.php cannot access the container and the entity manager and cannot store you user in the database if you don't declare it explicitly. The easiest way is to use the behat/symfony2-extension, then only you'll be able to use the entity manager and store an admin user. If you're using Composer then you can :

composer require --dev behat/symfony2-extension

Then you must activate the extension in your behat.yml like this :

default:
# ...
    extensions:
        Behat\Symfony2Extension: ~

And only then you can make your FeatureContext.php aware of the container so that it can access the entity manager. Add it right inside your FeatureContext class like this :

class FeatureContext extends RawMinkContext implements Context, SnippetAcceptingContext
    {
        use \Behat\Symfony2Extension\Context\KernelDictionary;
    ...
    }

Now you can call the entity manager and store your admin user :

/**
 * @Given there is an admin user :username with password :password
 */
public function thereIsAnAdminUserWithPassword($username, $password)
{   // Create an Admin User
    $user = new \AppBundle\Entity\User();
    $user->setUsername($username);
    $user->setPlainPassword($password);
    $user->setEmail("example@mail.com");
    $user->setRoles(array('ROLE_ADMIN'));
    $user->setEnabled(true); 

    // Store it in the database
    $em = $this->getContainer()->get('doctrine')->getManager();
    $em->persist($user);
    $em->flush();
}

Now you can use your test ! Don't forget to clean the database before you launch the test otherwise on your second try you'll get a nice error saying that this user already exists ! The best is to add a clearData() function that launches before every scenario like this one :

/**
 * @BeforeScenario
 */
public function clearData()
{
    $em = $this->getContainer()->get('doctrine')->getManager();
    $em->createQuery('DELETE FROM AppBundle:User')->execute();
}

Good luck !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM