BDD with Behat for Symfony2

Behavior Driven Development is an exciting new method of creating software. At LeaseWeb we work with roles like Product Owners, Functional Designers and Testers. These people have no access to the code repository. With BDD (Behavior Driven Development) this might change. BDD allows the Product owner to write the user stories in files with a .feature extension. An example of a feature definition is written below:

Feature: customer_list
  In order to reproduce a customers problem
  As a LeaseWeb employee working in dashboard
  I need to see a list of customers

Scenario: List customers in the dashboard
  Given I am logged in as a employee with username "bob" and password "i<3alice"
  And there is a customer with number "1234567890"
  And there is a customer with number "1234567892"
  When I get redirected after login
  Then I should get a list showing:
    """
    1234567890
    1234567892
    """

As you can see the product owner clarified the user story by describing a scenario. He might add more scenario’s and even backgrounds. Wouldn’t it be really cool if the software developers could implement these scenario’s and the product owner could see the progress by running this file? This is exactly what Behat offers. Software developers implement the methods that Behat generates. By default all generated functions throw a PendingException:

 /**
  * @Given /^I am logged in as a employee with username "([^"]*)" and password "([^"]*)"$/
  */
public function iAmLoggedInAsAEmployeeWithUsernameAndPassword($username, $password)
{
  throw new PendingException();
}

Software developers will replace those throw statements with real implementation like this (this example is using Mink):

 /**
  * @Given /^I am logged in as a employee with username "([^"]*)" and password "([^"]*)"$/
  */
public function iAmLoggedInAsAEmployeeWithUsernameAndPassword($username, $password)
{
  $this->visit('/login');
  $this->fillField('_username', $username);
  $this->fillField('_password', $password);
  $this->pressButton('login');
}

After implementing this function the product owner can run the feature with the “behat” command of Symfony2. The product owner sees the feature where every step of the scenario is colored. Green steps are implemented, brown steps are not yet implemented (Pending) and blue ones are skipped because they depend on pending steps. At the end of the run Behat shows a summary:

1 scenario (1 pending)
5 steps (1 passed, 3 skipped, 1 pending)
0m0.855s

Why BDD might make a huge difference:

  • Stories and scenario’s are added to the repository and can: easily be found, verified and related to a version or release.
  • It enables the product owner to get a visual and understandable view of the progress of the team.
  • The team will write good user stories and scenario’s since they are an important part of the development process.

And in a way it makes user stories and scenario’s “fun” for developers and product owners.

Read more about this topic:

Installation instructions:

Add git repositories to the “deps” file and update vendors afterwards with “bin/vendors install”. If your project is based on a Standard Edition that includes vendors you cloud consider cloning the repositories manually into the target directories.

In the “deps” file add:

...

[BehatBundle]
  git=https://github.com/Behat/BehatBundle.git
  target=/bundles/Behat/BehatBundle

[MinkBundle]
  git=https://github.com/Behat/MinkBundle.git
  target=/bundles/Behat/MinkBundle

You also need to install pear and the required packages.
IMPORTANT: sometimes this process hangs for several minutes, be prepared to have a lot of patience!

$ sudo apt-get install php-pear php5-curl php5-dev
$ sudo pear upgrade pear
$ sudo pear channel-discover components.ez.no
$ sudo pear channel-discover pear.symfony-project.com
$ sudo pear channel-discover pear.phpunit.de
$ sudo pear install --alldeps phpunit/PHPUnit
$ sudo pear channel-discover pear.symfony.com
$ sudo pear channel-discover pear.behat.org
$ sudo pear install --alldeps behat/behat
$ sudo pear install --alldeps behat/mink

In app/AppKernel.php:

public function registerBundles()
{
  ...
  if ($this->getEnvironment() === 'test') {
    $bundles[] = new Behat\BehatBundle\BehatBundle();
    $bundles[] = new Behat\MinkBundle\MinkBundle();
    if (!defined('BEHAT_AUTOLOAD_SF2')) define('BEHAT_AUTOLOAD_SF2', false);
    require_once 'behat/autoload.php';
    require_once 'gherkin/autoload.php';
    require_once 'mink/autoload.php';
  }
  return $bundles;
}

In app/autoload.php:

$loader->registerNamespaces(array(
  ...
  'Behat\\BehatBundle'=>__DIR__.'/../vendor/bundles',
  'Behat\\MinkBundle'=> __DIR__.'/../vendor/bundles',
));
Share

Fast Symfony2 served on a stable Xubuntu

Ingredients:

Introduction:

This recipe is for people who want to install symfony2 quickly with all dependencies, without having to read the excellent documentation that can be found here:

  1. The quick tour
  2. The book
  3. The cookbook

This recipe can be used by linux users or by windows users that run linux in a virtual machine.

This will get you:

  • A working Symfony2 installation
  • Pretty URL’s (using mod_rewrite)
  • Optimized execution speed (using php5-apc)
  • Configured timezone settings and internationalization support (using php5-intl)
  • Run as a specific user and not as www-data (using apache2-mpm-itk)
  • Created a database and installed a database management tool (phpmyadmin).

The instructions are meant for a development platform. If you use these instructions on a production platform the least you should do is choose a more secure password. NB: You also might want to run Ubuntu Server instead of Xubuntu in such a case.

This will NOT get you:

  • A debugging environment (using XDebug)
  • An PHP IDE with debugging integration (Eclipse PDT)

For serious web development you might want the above: a full-featured IDE and a step-by-step debugger. This will be discussed in another post.

Instructions:

The lines below with a “-” are actions and the lines with a “$” are commands:


- Download and install Virtualbox
- Download xubuntu iso
- Configure a new Virtual Machine with:
  OS set to: Linux/Ubuntu 64
  4 cores (assuming you have 8 cores)
  2048 MB RAM (assuming you have at least 4096)
  20 GB VDI dynamic disk
- Select the Xubuntu iso file in the first run wizard
- Install Xubuntu by following the steps
- Install additional drivers (guest additions) and reboot
- Open up a web browser
- Go to http://symfony.com/download and click download
- Save "Symfony_Standard_Vendors_2.x.xx.tgz" and unpack it
- Move the "Symfony" folder to your home directory and rename it to "public_html"
$ sudo apt-get install lamp-server^ php-apc php5-intl php5-sqlite phpmyadmin
 choose a mysql root password
 choose to configure phpmyadmin for apache2
$ sudo apt-get install apache2-mpm-itk
$ sudo a2enmod rewrite
$ sudo nano /etc/php5/apache2/php.ini
 NB: use Ctrl-W Ctrl-R to search and replace
 search: ;date.timezone =
 replace: date.timezone = Europe/Amsterdam
 NB: enter your local timezone if you are not in Europe/Amsterdam
 (see http://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
 search: short_open_tag = On
 replace: short_open_tag = Off
$ pwd
 NB: note down the output, as this is the path of your home folder (in my case: /home/maurits)
$ id -un
 NB: note down the output, as this is your user name (in my case: maurits)
$ id -gn
 NB: note down the output, as this is your group name (in my case: maurits)
$ sudo nano /etc/apache2/sites-enabled/000-default
 NB: use Ctrl-W Ctrl-R to search and replace
 search: /var/www
 replace: {YOUR_HOME_FOLDER}/public_html/web
 NB: you have to enter the home folder path you found from the pwd command instead of {YOUR_HOME_FOLDER}
$ sudo nano /etc/apache2/sites-enabled/000-default
 NB: add the following line under the DocumentRoot
 AssignUserId {YOUR_USER_NAME} {YOUR_GROUP_NAME}
 NB: you have to enter your user name and group name instead of {YOUR_USER_NAME} {YOUR_GROUP_NAME}
$ sudo service apache2 restart
- Open web browser and go to http://127.0.0.1/phpmyadmin
- Login with root user and chosen root password
- Click "Privileges"
- Click "Add a new User"
- Enter/choose:
 username: symfony
 host: localhost
 password: symfony
 retype: symfony
- Select "Create database with same name and grant all privileges"
- Click "Create User"
- Open a web browser and go to http://127.0.0.1/config.php
- verify that symfony does not complain about anything
- click "Configure your Symfony Application online"
- Enter/choose:
 Driver: MySQL (PDO)
 Host: localhost
 Name: symfony
 User: symfony
 Password: symfony
 Password again: symfony
- Let symfony generate a "Global Secret"
- Choose "Next Step"
- Symfony should say: Your distribution is configured!
- Click "Go to the Welcome page"
- Click "Run the Demo"
- Play around with the demo to see that everything works

Bonus instructions:

Some nice tips on how to customize your Xubuntu:

That is it for today, have fun!

Share

Painless (well, less painful) migration to Symfony2

Over the past months I’ve talked to several companies that run a symfony 1 codebase, sometimes as old as symfony 1.0, which is big, very big. Often these applications or websites are at the heart of their business. Yet, Symfony2 is stable now, and symfony 1 support is going to stop at some point. Regularly, they ask me for advice on migrating their legacy codebase to a brand new Symfony2 project. Often enough, I have to inform them that migration from a symfony 1 to a Symfony2 codebase is a lot of effort and a huge investment in time (and therefore money). Many companies then wonder whether they should do it at all. The thing is, there is no need to do it all at once.

Gradual migration

It is much easier to do a gradual migration. Start with one part of your application, and bit by bit migrate your logic and application. The traditional way of doing such migrations is to create a new project and have parallel development on the old and the new version of the application. The problem with this, though, is that when you make a change to your old application, you have to make the same change in the new codebase, essentially doubling the amount of work for each feature you need to implement.

Wouldn’t it be much cooler if you only need to change this once? This is of course an option. You could wrap your old application into your Symfony2 application, and have different parts of your application be handled by different versions of your codebase. Once you’ve ported a new section of your application to Symfony2, you change the routing in your application to point those URLs to your Symfony2 bundle instead of your old symfony 1 project, and that part is done. You can move on to the next part.

A word of caution

While this approach is ideal because you can migrate in several phases, there are some issues with this approach. First of all, this will have a performance impact on your application. Instead of a single framework, you now use two frameworks for all your legacy pages. Result: An extra overhead because, well, two frameworks add more overhead than one. And though Symfony2 is in this case only a proxy, it does add an extra layer. Of course, you can use the cool caching features of Symfony2 to add a caching layer in front of your symfony 1 application (which might actually speed up your application), but this definitely does not apply to all projects. So before simply applying some caching to your application, think about whether it will work. And keep testing 🙂

Another issue is sessions. If your application works with authentication and authorization, you’ll now have to work with two different systems that have a different approach to authentication and authorization. So far, I have not yet figured out how to manage this, but in the projects I’ve used this approach for so far, I didn’t really need to do this. But this is something you really need to consider.

A tool

I created a simple bundle that could assist in using this approach. The IngewikkeldWrapperBundle adds a simple fallback route in your Symfony2 application, so that any URL not handled by your Symfony2 application is automatically forwarded to your legacy codebase. The legacy codebase handles the request and returns the response, and that’s it. And once you’ve had the time to work on porting a new part of the codebase to Symfony2, you simply add a route to your new bundle that matches the old URL pattern for that part of your application, and Symfony2 starts serving that part instead of symfony 1.

Share