Joeri Verdeyen bio photo

Joeri Verdeyen

Web-engineer, cyclist, Nespresso lover, Strava pusher.

Twitter LinkedIn Instagram Github Stackoverflow Last.fm Strava

Behat testing with PhantomJS and Robo Task Runner

Stack

In this post I’ll give you a quick look on my current behaviour testing workflow within my Symfony2 projects. I’m currently using the following stack for this workflow:

  • Symfony2: my PHP weapon of choice
  • Behat: testing the behaviour of my applications
  • PhantomJS: a fast and headless webkit driver
  • Robo Task Runner: a fast PHP task runner
  • Composer: a PHP package manager

Symfony2 AcmeDemoBundle

I’m starting this example from an empty Symfony2 project, you can follow the Installing and Configuring Symfony2 Cookbook. So make sure you can visit the /demo/hello/{name} path.

Composer

Add the following packages to your composer.json. Run composer update afterwards.

 1 "require": {
 2     ...
 3     "behat/behat": "v2.5.2",
 4     "behat/mink":   "*",
 5     "behat/symfony2-extension": "v1.1.2",
 6     "behat/mink-extension": "~1.2.0",
 7     "behat/mink-goutte-driver": "*",
 8     "behat/mink-browserkit-driver": "~1.1.0",
 9     "behat/mink-zombie-driver": "*",
10     "behat/mink-selenium2-driver": "v1.1.1",
11     "codegyre/robo": "dev-master"
12 },
$ composer update

or

$ php composer.phar update

NPM (Node.js)

I’m using project specific npm packages, so you’ll have to install Node.js. Create a package.json file in the root of your project folder with the following content. Afterwards run npm install to install the packages.

 1 {
 2   "name": "AcmeDemoProject",
 3   "version": "1.0.0",
 4   "private": true,
 5   "devDependencies": {
 6     "phantomjs": "~1.9.0"
 7   },
 8   "dependencies": {
 9     ...
10   }
11 }

Check that it works by issuing ./node_modules/.bin/phantomjs --version in console.

$ ./node_modules/.bin/phantomjs --version
1.9.7

Behat

To describe your different behat testing configurations create a behat.yml file in your project folder. I prefer to place this in the app/config folder.

 1 # app/config/behat.yml
 2 
 3 default:
 4     formatter:
 5         name:           pretty,junit,html
 6         parameters:
 7             output_path:    null,build/logs/behat,build/behat_report.html
 8     extensions:
 9         Behat\Symfony2Extension\Extension:
10             mink_driver: true
11             kernel:
12                 env: dev
13                 debug: true
14         Behat\MinkExtension\Extension:
15             base_url: 'http://localhost:8000/app_dev.php'
16             default_session: selenium2
17             javascript_session: selenium2
18             files_path: %behat.paths.base%/../../src/Acme/DemoBundle/Features/files
19             selenium2:
20                 wd_host: "http://localhost:4444/wd/hub"
21                 browser:              "firefox"
22                 capabilities:         { "browserName": "firefox", "browser": "firefox", "version":  "22" }
23     paths:
24         features: features

A simple test

Create a simple feature test in src/Acme/DemoBundle/Features/.

 1 # src/Acme/DemoBundle/Features/hello.feature
 2 
 3 Feature: Visit the helo page
 4     In order to visit the helo page
 5     As a normal user
 6     I need to go to that page
 7 
 8 Scenario: Visit the helo page
 9     Given I am on "/demo/hello/barry"
10     Then I should see "Hello barry!"

Symfony2 test client

Enable the test client in the Symfony2 framework config. Add the following in the config_dev.yml file.

1 # app/config/config_dev.yml
2 
3 framework:
4     test: ~

Robo

A RoboFile.php describes a series of tasks to execute. Put this in the root of your project folder.

 1 <?php
 2 // RoboFile.php
 3 
 4 class RoboFile extends \Robo\Tasks
 5 {
 6 
 7     public $basedir = __DIR__;
 8 
 9     public function build()
10     {
11         $this->prepare();
12         $this->behat();
13     }
14 
15     public function prepare()
16     {
17 
18         $dirs = array(
19             "build/behat",
20             "build/logs/behat"
21         );
22 
23         $task = $this->taskExecStack()->stopOnFail();
24 
25         foreach ($dirs as $dir) {
26             $task->exec("mkdir -p {$dir}");
27         }
28 
29         $task->run();
30 
31         $this->taskComposerInstall()
32             ->optimizeAutoloader()
33             ->run();
34 
35         $this->npm();
36 
37         $this->taskExec('app/console assets:install')->run();
38         $this->taskExec('app/console assetic:dump')->run();
39 
40     }
41 
42     private function npm()
43     {
44         $this->taskExec('npm')->arg("install")->run();
45     }
46 
47     private function behat()
48     {
49 
50         $this->taskExec('./node_modules/.bin/phantomjs --webdriver=4444 --webdriver-loglevel=WARNING')
51                 ->background()
52                 ->run();
53 
54         $this->taskExec('app/console server:run localhost:8000')
55                 ->background()
56                 ->run();
57 
58         $this->taskExec('./bin/behat @AcmeDemoBundle -c app/config/behat.yml')
59                 ->run();
60     }
61 }

Start testing

Execute your Robot tasks

$ ./bin/robo build

...
» 1 scenario»  (1 passed)
» 2 steps»  (2 passed)

Footnote

This is just a very basic setup of what you can achieve with those tools. Robo can run all of your tests, so you can add this in your ci environment. You can create your own feature contexts to customize specific actions. This setup isn’t limited to Symfony2 only, you can easily create this in a legacy ‘plain’ PHP project, which I recommend doing.

Thanks for reading

Feel free to leave a comment if you have remarks or like this post