Creating User Data From Behat Background - Part 2


Following on from the previous video, in this part we will write the code that populates the thereAreUsersWithTheFollowingDetails function in our UserSetupContext.

By the end of this video, we should have the following in our UserSetupContext:

<?php

// src/AppBundle/Features/Context/UserSetupContext.php

namespace AppBundle\Features\Context;

use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\TableNode;
use Doctrine\ORM\EntityManagerInterface;
use FOS\UserBundle\Model\UserManagerInterface;

class UserSetupContext implements Context, SnippetAcceptingContext
{
    /**
     * @var UserManagerInterface
     */
    private $userManager;
    /**
     * @var EntityManagerInterface
     */
    private $em;

    public function __construct(UserManagerInterface $userManager, EntityManagerInterface $em)
    {
        $this->userManager = $userManager;
        $this->em = $em;
    }

    /**
     * @Given there are Users with the following details:
     */
    public function thereAreUsersWithTheFollowingDetails(TableNode $users)
    {
        foreach ($users->getColumnsHash() as $key => $val) {

            $user = $this->userManager->createUser();

            $user->setEnabled(true);
            $user->setUsername($val['username']);
            $user->setEmail($val['email']);
            $user->setPlainPassword($val['password']);

            $this->userManager->updateUser($user);

            $qb = $this->em->createQueryBuilder();

            $query = $qb->update('AppBundle:User', 'u')
                ->set('u.id', $qb->expr()->literal($val['uid']))
                ->where('u.username = :username')
                ->andWhere('u.email = :email')
                ->setParameters([
                    'username' => $val['username'],
                    'email'    => $val['email']
                ])
                ->getQuery();

            $query->execute();
        }
    }
}

Let's walk through what's happening here in a little more depth.

We injected the UserManager, which is the user manager created for us when we configured FOSUserBundle. We also inject the 'standard' Doctrine entity manager so we can update this user outside the scope of FOSUserBundle's normal operations.

Behat has the concept of tables of data:

  Background:
    Given there are Users with the following details:
    | uid | username | email          | password |
    | u1  | peter    | peter@test.com | testpass |
    | u2  | john     | john@test.org  | johnpass |

This data is passed to our function as a TableNode which we are calling $users, simply because that's what this table node represents.

I have covered Table Nodes in more detail on the blog, so be sure to read up on that post if you are unsure on the concept of Table Nodes, or what sort of data they provide.

The first row in a Table Node is considered to contain the titles of the data. In our case, this would be uid, username, email, and password. These values will be used as keys in our array, which we will use when looping through our data.

Once inside our loop, the standard procedure of creating a User with FOSUserBundle is followed as normal.

$user = $this->userManager->createUser();

$user->setEnabled(true);
$user->setUsername($val['username']);
$user->setEmail($val['email']);
$user->setPlainPassword($val['password']);

$this->userManager->updateUser($user);

We create a User using the user manager. This creates an 'empty' user, which we then populate - and importantly, enable. We could set the value of enabled to also be passed in via the table, but for the purposes of this test feature, we always expect our Users to be enabled.

Be sure to set the plain password here, rather than simply the password - if unsure on this, be sure to watch the FOSUserBundle tutorial course here at CodeReviewVideos.

Changing The User ID

Where this step gets more interesting is in we are changing the User's ID.

First, we must have saved the User to the database. We do this in the $this->userManager->updateUser($user); line.

At this point, the user will have received an automatically generated ID - something long and -crazy- unique. This is great in the real world, but not so good for us in test.

In test, we want to control the ID so we can then use it in subsequent stages for relationships, and also for access via our API end points. If we don't control this ID, the rest of our testing becomes impossible.

To change the ID, we need to write a standard Doctrine update statement:

$qb = $this->em->createQueryBuilder();

$query = $qb->update('AppBundle:User', 'u')
    ->set('u.id', $qb->expr()->literal($val['uid']))
    ->where('u.username = :username')
    ->andWhere('u.email = :email')
    ->setParameters([
        'username' => $val['username'],
        'email'    => $val['email']
    ])
    ->getQuery();

$query->execute();

Hopefully nothing new here, but simply finding the user by their username and email address, and updating their id to the one we want it to be.

We use the $qb->expr()->literal($val['uid']) syntax which is rather unusual looking, so we don't need to pass in a parameter. You are free to do this how you like, but this is the way I tend to do it.

After this, your feature should run and populate the fos_users table with the expected rows, with their expected IDs.

From here we can ramp up the speed somewhat and start adding in more entity creation steps without reviewing them in quite so much detail.

Code For This Course

Get the code for this course.

Episodes