Creating User Data From Behat Background - Part 1
In this two part video we will write the code behind our Behat User feature background step. More specifically, we will cover the following Background step:
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 |
We will cover this in quite some detail. Once you understand this step, the remaining database table steps in the Background phase are very similar.
As mentioned already in this series, Behat is quite a slow starter. There's a heck of a lot of boilerplate code to write for your first few features, which thankfully becomes quite easy to copy / paste as your project grows. Not to mention, as your confidence and familiarity with Behat increases, writing new features and step definitions becomes second nature.
FOS User Bundle Setup
I'm not going to go into great depths about setting up FOSUserBundle. If any of this is new to you, or you are unsure about FOSUserBundle in general then be sure to check out my Getting Started with FOSUserBundle tutorial series.
Whilst we covered the composer dependencies for this project in an earlier video, we didn't enable or configure any of the dependencies at that point. Let's fix that now.
// app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
// our third party dependencies
new FOS\RestBundle\FOSRestBundle(),
new Nelmio\CorsBundle\NelmioCorsBundle(),
new Nelmio\ApiDocBundle\NelmioApiDocBundle(),
new JMS\SerializerBundle\JMSSerializerBundle(),
new FOS\UserBundle\FOSUserBundle(),
// not using these currently, but will enable shortly
// new Csa\Bundle\GuzzleBundle\CsaGuzzleBundle(),
// new Oneup\FlysystemBundle\OneupFlysystemBundle(),
// new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
new AppBundle\AppBundle(),
];
if (in_array($this->getEnvironment(), ['dev', 'test', 'acceptance'], true)) {
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
}
return $bundles;
}
// etc
We'll need to add in some new parameters, so be sure to update your parameters.yml.dist
, as well as your parameters.yml
files:
# app/config/parameters.yml
parameters:
# standard symfony parameters here have been removed
# nelmio cors
cors_allow_origin: 'http://symfony-rest-example.dev'
# nelmio api docs
api_name: 'Symfony 3 REST Example'
api_description: 'Some clever description goes here'%
And we will need to add the expected config to our config.yml
file:
# app/config/config.yml
# standard Symfony config removed for brevity
# Nelmio CORS
nelmio_cors:
defaults:
allow_origin: ["%cors_allow_origin%"]
allow_methods: ["POST", "PUT", "GET", "DELETE", "OPTIONS"]
allow_headers: ["content-type", "authorization"]
max_age: 3600
paths:
'^/': ~
# Nelmio API Doc
nelmio_api_doc:
sandbox:
accept_type: "application/json"
body_format:
formats: [ "json" ]
default_format: "json"
request_format:
formats:
json: "application/json"
# FOS User Bundle
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: AppBundle\Entity\User
# FOS REST Bundle
fos_rest:
body_listener: true
format_listener: true
param_fetcher_listener: true
view:
view_response_listener: 'force'
exception_wrapper_handler: null
formats:
jsonp: true
json: true
xml: false
rss: false
mime_types:
json: ['application/json', 'application/x-json']
jpg: 'image/jpeg'
png: 'image/png'
jsonp_handler: ~
routing_loader:
default_format: json
include_format: false
format_listener:
rules:
- { path: ^/, priorities: [ json, jsonp ], fallback_format: ~, prefer_extension: true }
exception:
enabled: true
# JMS Serializer
jms_serializer:
metadata:
directories:
FOSUB:
namespace_prefix: FOS\UserBundle
path: %kernel.root_dir%/serializer/FOSUB
We will add the rest of the config as we get to it in future videos, but for now, these are the 'core' pieces we will need. Largely this is copy and paste from the various documentation files for each of the projects.
If you haven't used FOSRESTBundle before, be sure to check out my course on FOSRESTBundle.
At this stage we haven't actually created a User
entity to make our fos_user
config work, but as you will soon see, this will follow shortly.
UserSetupContext
- Not Behat Best Practice
It's important to point out that the following step is not Behat best practice - to the very best of my knowledge.
Behat contexts are all about the describing the situation in which the test is taking place. It might be that we have run our tests in the 'context' of our RESTful API, or a separate set of tests that run in the 'context' of a user browsing our site via the standard web browser.
With this in mind, it makes no sense to have a UserSetupContext
. I accept this is wrong, but it helps me structure and organise my test files. Primarily I have one *SetupContext
per entity / resource that will be in use. This works for me. You may wish to group all your setup into one file, or ignore this practice entirely.
As demonstrated in the video, we will use PHPSpec to describe and create our User
entity. This is a standard FOSUserBundle User Entity, with the only difference being that we are controlling the UserInterface
rather than relying on the one provided by FOSUserBundle.
By the end of this video we will have the following:
<?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)
{
}
}
In the next part, we will populate the thereAreUsersWithTheFollowingDetails
function.