Behat and Drupal Extension at first touch

Case type:

Service area: 

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