Behat and Drupal Extension at first touch
Case type:
Technologies:
I was really impressed, back in 2013, by Alexandru Badiu's (@voidberg) presentation on Behat delivered at Drupalcamp Târgu Mureș. This year, Konstantin Kudryashov (@everzet), the guy behind Behat, Mink & phpspec2, had its own presentation at Drupalcon Amsterdam. And it was again — Wow, wow! Since then I never found the right time to play and experiment with Behat until I got stuck in providing a tested UI for a site having very complex frontend/jQuery requirements. And for me that was the right time for Behat :)
More than testing
But wait! Did I said ‟testing″? Yes, but Behat is not only about testing, it’s more than that. It’s the PHP framework for a software development process called Behavior-Driven Development. And that is a very interesting and revolutionary concept that proposes a way to manage the software development by both sides: business and technical.
Yes, BDD is a management subject. You can find out more by watching the Konstantin Kudryashov’s presentation video recorded at Drupalcon Amsterdam 2014.
So, what we got here? This short writeup is about providing a Behat based testing environment for Drupal and start doing functional testings.
Behat, Mink, Selenium Server & Drupal Extension
These are the ingredients that we’ll put together. Just two words about each:
- Behat: Is an open source behavior-driven development framework for PHP (>= 5.3).
- Mink: Is a browser emulator abstraction layer that supports both, in-browser and headless browser emulators.
- Selenium Server: Is a browser automation tool that let’s you perform real actions in real browsers.
- Drupal Extension (for Behat and Mink): Provides Drupal-specific functionality for the Behavior-Driven Development testing frameworks of Behat and Mink.
Of course you can extend the Drupal support provided by Drupal Extension.
Requirements
PHP
You’ll need PHP 5.3.5. Check you installation and upgrade it if necessary:
$ php --version
PHP extensions
At least next extensions are mandatory:
- curl
- mbstring
- xml
Composer
Install Composer globally, if you have not already done so. Find out how here.
Java
Java is needed by Selenium Server. Apparently it doesn’t matter what version, just type the command to make sure you have Java on your system.
$ java -version
cURL
Make sure it’s installed:
$ curl --version
Selenium
I downloaded the Selenium Server from http://docs.seleniumhq.org/download/. When I wrote this, 2.44.0 was the latest stable version. Now, all you need to do is to put the .jar
file in a good place ( /path/to/selenium
) on your server and then start the Selenium Server:
$ java -jar /path/to/selenium/selenium-server-standalone-2.44.0.jar
Note that when I did that, it failed with the next error:
Failed to start: [email protected]:4444 Exception in thread "main" java.net.BindException: Selenium is already running on port 4444.
It seems that somehow I started the server twice. Maybe I clicked the .jar file after downloading in browser? Anyway, the hint ‟How to handle Selenium is already running on port 4444 ?” from Assert Selenium — Tips & Tricks helped me to fix the issue.
Behat & Drupal Extension
In this case I learn you how to install the entire suite globally (system wide). While Drupal Extension requires Behat, Mink, and the Mink Extension, we have only to install Drupal Extension and, magically, Composer will do the rest. Drupal Extension was written and is maintained by Jonathan Hedstrom (@jhedstro).
Create a home for the suite:
$ cd /opt $ sudo mkdir drupalextension $ cd drupalextension
Create there a file called composer.json
having next content:
{ "require": { "drupal/drupal-extension": "~3.0" }, "config": { "bin-dir": "bin/" } }
Then run the installer:
$ sudo composer install
Make Behat binary accessible from the system:
$ sudo ln -s /opt/drupalextension/bin/behat /usr/local/bin/behat
Test your installation:
$ bin/behat --help
Project tests
OK. We have now Selenium server running, we have Behat and Drupal Extension installed and working. So, what’s next? It’s time now to configure the place where we’ll put our project-specific Behat tests.
Let’s create a directory inside the project structure. This doesn't necessary to be under Drupal webroot but it's better to have it inside VCS (Git, SVN, etc). If your Drupal webroot level is the same as the VCS top level directory (maybe most of the cases) you can create it in sites/default/behat
(or a specific site directory for multi-sites). In my case the Git top directory is one level above the Drupal webroot, so I preferred to create the directory outside Drupal.
In order to illustrate we’ll consider the next structure:
/path/to/project
— The project directory (under which the .git directory exists)/path/to/project/drupal
— The Drupal webroot directory./path/to/project/behat
— The project Behat testinglocations.http://localhost
— The website host.
# Drupal webroot directory is bellow Git top directory. $ cd /path/to/project
Or
# Git and Drupal top directories are the same. $ cd /path/to/project/drupal/sites/default
$ mkdir behat $ cd behat
Create the behat.yml
file with the next content:
default: suites: default: contexts: - FeatureContext - Drupal\DrupalExtension\Context\DrupalContext - Drupal\DrupalExtension\Context\MinkContext - Drupal\DrupalExtension\Context\MessageContext - Drupal\DrupalExtension\Context\DrushContext extensions: Behat\MinkExtension: goutte: ~ selenium2: ~ base_url: http://localhost Drupal\DrupalExtension: blackbox: ~
Let Behat initialize the directories and files needed for this testing environment:
$ behat --init
This will command will create the FeatureContext.php
file in features/bootstrap/
sub-directory. The features/
directory is were you’ll place the Behat features containing your testing scenarios.
Let’s see if everything is fine:
$ behat -dl default | Given I am an anonymous user default | Given I am not logged in default | Given I am logged in as a user with the :role role(s) default | Given I am logged in as :name default | Given I am logged in as a user with the :permissions permission(s) default | Then I should see (the text ):text in the ":rowText" row ...
This will print all available step definitions. You will notice that some of them have Drupal flavour while others are applicable to any website. The first category definitions are provided by Drupal Extension and the other are provided by Mink Extension. Remember — you can expand this list with your own project step definitions.
Drivers
Drupal Extension provides 3 drivers for testing your Drupal 6, 7 and 8 sites: Blackbox, Drush and Drupal API:
- Blackbox: This driver gives you no privileged access to the site. It’s perfect for anonymous user tests. This doesn’t mean that you can’t use this driver for logged in operations. You can but all user creation and login would have to take place via the user interface and that is time consuming.
- Drush: This driver let’s you add users, login, reset passwords. This can be done also with Drupal API driver and in a most advanced way but Drush driver has the advantage that allows testing sites living on remote servers.
- Drupal API: This is the fastest and more powerful driver but has this limitation: it cannot perform tests against remote servers.
On Drupal Extension documentation site there’s a nice comparison chart that you may want to check before deciding what driver to use. As a recommendation, you can consider using Blackbox for testing anonymous functionality and Drupal API driver for authenticated users actions.
Looking back at our behat.yml
configuration file, you can observe that the Blackbox driver has been already added under default.extensions.Drupal\DrupalExtension
. Now, let’s tell Behat that we want to use also the Drupal API driver. We need to add this under Drupal\DrupalExtension
key:
Drupal\DrupalExtension: blackbox: ~ api_driver: 'drupal' drupal: drupal_root: /path/to/project/drupal
The entire behat.yml
(under /path/to/project/behat
) will look now:
default: suites: default: contexts: - FeatureContext - Drupal\DrupalExtension\Context\DrupalContext - Drupal\DrupalExtension\Context\MinkContext - Drupal\DrupalExtension\Context\MessageContext - Drupal\DrupalExtension\Context\DrushContext extensions: Behat\MinkExtension: goutte: ~ selenium2: ~ base_url: http://localhost Drupal\DrupalExtension: blackbox: ~ api_driver: 'drupal' drupal: drupal_root: /path/to/project/drupal
Your first scenario
Now, you are ready to write your first scenario. Writing testing scenarios is simple. Under features directory (/path/to/project/behat/features/
) create a feature file. Let’s say first.feature
with this content:
Feature: first My first Behat try. Don’t laugh :) Scenario: The Drupal installer should enable the footer block Given I am an anonymous user Given I am on the homepage Then I should see "Powered by Drupal" @api Scenario: On the account page the account age is displayed Given I am logged in as a user with the "authenticated user" role When I click "My account" Then I should see "Member for"
Feature files are using special language called Gherkin to describe scenarios and steps. I will not get into details on how to write feature files for Behat but as you can see it’s straight and obvious. But you should still check the Behat documentation for details.
One thing I want you to notice here. The second scenario is marked with the @api
tag. That means that it will use the Drupal API driver from Drupal Extension (api_driver: 'drupal'
). Removing the tag there will cause the test to fail because Blackbox is not able to handle user creation and logging in behind the scenes.
When you’ll need to test JavaScript behavior, you’ll have to do that on real browsers. For that you’ve already installed the Selenium Sever. To enable the power of real browser testing, you’ll just need to tag your scenarios with the @javascript
tag and the tests will user Selenium to run in-browser tests.
Simple, isn’t it?
Run!
The great moment has come:
$ cd /path/to/project/behat $ behat
You should check the documentation for additional Behat command line parameters.
Your step definitions
On your project you can write your own specific definitions. I will not cover this topic here (this is only a ‟first touch” writeup, remember?) but just to illustrate how you can do that, add the next scenario to your first.feature
file and run the test. Then check the test output to find out how Behat is helping you to write new steps:
Scenario: The E-commerce functionality works good Given I am a shop manager Then I should be able to add new products
Amazing! :)
End
Installing and starting tests with Behat is easy, as you can see. As this writeup is only a thin start, you’ll need to go deeper by reading the Behat and Drupal Extension documentation. Don’t forget to learn about Beahavior-Driven Development (BDD), it might change the way you and your client are managing the software project.
Resources
- Behat — The official website.
- The Drupal Extension to Behat and Mink, by Jonathan Hedstrom.
- Doing Behaviour-Driven Development with Behat, by Konstantin Kudryashov.
- Behavior-Driven Development with Drupal, by Alexandru Badiu.