I strongly suggest you read part 1 where I explain how each of these libraries and applications interact with each other.
Part 1: Automated Drupal testing with Behat, Selenium, and Headless FirefoxI assume you have the following already installed:
We'll let Composer handle the majority of the work. We'll need to tell Composer what dependencies our project requires.
Create a composer.json
file that contains all our required packages by pasting in the following:
{ "require": { "behat/behat": "~3.0", "behat/mink-goutte-driver": "~1.2.0", "behat/mink-extension": "~2.1", "behat/mink-selenium2-driver": "~1.3", "drupal/drupal-extension": "~3.1" } }
Have Composer install our needed packages. From the same directory the composer.json
file is in, run:
composer install
If you get an error like the following:
PHP Fatal error: Uncaught exception 'ErrorException' with message 'proc_open(): fork failed - Cannot allocate memory'
Composer executes PHP on the CLI so it should be configured to have no memory limit by default thus exhausting all the machine has to offer. You will need to enable Virtual Memory (Swap File).
Behat will handle all the actual test cases. We'll configure some path settings and have Behat initialize our directory structure.
Create a new file called behat.yml and paste in the following:
default: autoload: '': %paths.base%/features/bootstrap suites: default: paths: - %paths.base%/features contexts: - FeatureContext - Drupal\DrupalExtension\Context\DrupalContext - Drupal\DrupalExtension\Context\MinkContext - Drupal\DrupalExtension\Context\MessageContext extensions: Behat\MinkExtension: goutte: ~ selenium2: ~ base_url: https://drupal.org/ Drupal\DrupalExtension: blackbox: ~ region_map: header_nav: ".wx-header-tools"
We're using Behat 3.x so the autoload
and path
settings look a little different from the old 2.5 version.
Notice the use of blackbox
. It comes with the DrupalExtension; it allows us to find elements within a "region" for situations where the target element doesn't have a unique selector. See Blackbox docs for more information.
Now that we have our behat.yml file configured, let's have Behat initialize a working directory tree.
./vendor/bin/behat --init
You should get some green outputs about where to put features and context classes.
Nothing special here. Just use apt-get to install the current stable release of Firefox.
sudo apt-get install firefox
We'll use Xvfb to create a dummy display for Firefox to run in.
sudo apt-get -y install xvfb gtk2-engines-pixbuf sudo apt-get -y install xfonts-cyrillic xfonts-100dpi xfonts-75dpi xfonts-base xfonts-scalable
We need to configure a display number kind of high to avoid collision with existing displays.
sudo Xvfb :10 -ac -screen 0 1024x768x8 &
You may get a warning about missing font paths; this is okay, just ctrl+c. Verify Xvfb is running by running:
ps aux | grep -P "^root.*Xvfb"
You should get one or two lines back that look like:
root 11345 0.0 2.7 136480 13724 pts/0 S 00:24 0:00 Xvfb :10 -ac
We need to update the DISPLAY
environment variable so Firefox will know what to attach to.
export DISPLAY=:10
Download the latest jar
file.
curl -J -O -L http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.0.jar
We'll make a symbolic link to a filename without Selenium's version number.
ln -s ./selenium-server-standalone-2.53.0.jar ./selenium-server-standalone.jar
Selenium can be configured to run as a hub, a node, or standalone. For this tutorial, we'll configure the hub first, then add a node.
Create a script called start-selenium-hub.sh
that will start our Seleniun Hub.
Paste the following code in this new file:
#!/bin/bash java -jar ./selenium-server-standalone.jar -role hub -host 0.0.0.0 > ./selenium-hub.log 2>&1 &
Give the script executable permissions and run it:
chmod u+x ./start-selenium-hub.sh ./start-selenium-hub.sh
Notice we're outputting to a selenium-hub.log file. We'll do the same for the node.
Selenium binds to the 0.0.0.0 interface regardless of the host settings you provide. You should not run this on a publicly accessible machine without blocking external connections via a firewall or iptables.
Create a script called start-selenium-node.sh
that will start our Selenium node.
Paste the following code in this new file:
#!/bin/bash if [ ! -f /tmp/.X5-lock ]; then /usr/bin/Xvfb :10 -ac -screen 0 1024x768x8 & fi export DISPLAY=:10 # firefox needs this to know where to find a display to run on java -jar ./selenium-server-standalone.jar -role node -hub http://127.0.0.1:4444/grid/register/ > ./selenium-node.log 2> ./selenium-node.err &
Give the script executable permissions and run it:
chmod u+x ./start-selenium-node.sh ./start-selenium-node.sh
If Selenium fails to start fully and you are on a VPS hosting service such as Digital Ocean or a VM; see this entropy related problem and solution:
Selenium Node, slow hub register
We're almost ready to run our test scripts!
We're going to do a test search on drupal.org for our test.
When we ran /vendor/bin/behat --init
earlier, a file features/test.feature
was created. This is where we'll store our test cases.
Add the following to the features/test.feature
file:
Feature: Drupal.org search Searching for behat from drupal.org's search box. @javascript Scenario: Searching for "behat" Given I go to "https://www.drupal.org" When I search for "behat" Then I should see "Behat Drupal Extension"
We'll be testing Drupal.org's search box. For this, we need to write a custom step definition for: "I search for".
Earlier when we ran /vendor/bin/behat --init
, it generated a file: features/bootstrap/FeatureContext.php
. In this file, add the follow snippet:
/** * @When /^I search for "([^"]*)"$/ */ public function iSearchFor($search_term) { $page = $this->getSession()->getPage(); $page->fillField('Search', $search_term); $page->pressButton('Search'); }
Make sure you are currently in the directory that contains the behat.yml
. Run the following command:
vendor/bin/behat
You should get an output similar to:
Feature: Drupal.org search Searching for behat from drupal.org's search box. @javascript Scenario: Searching for "behat" # features/test.feature:5 Given I go to "https://www.drupal.org" # Drupal\DrupalExtension\Context\MinkContext::visit() When I search for "behat" # FeatureContext::iSearchFor() Then I should see "Behat Drupal Extension" # Drupal\DrupalExtension\Context\MinkContext::assertPageContainsText() 1 scenario (1 passed) 3 steps (3 passed) 0m9.66s (14.59Mb)
If you receive errors like:
Unable to connect to host 127.0.0.1 on port 7055 after 45000 ms.
It's likely your version of Selenium is not compatible with the version of Firefox you have installed. You'll need to downgrade to a previous version.
Here is an example of manually installing a previous release of Firefox.
Software engineer by profession, embedded systems tinkerer, husband, father, fantasy novel devourer, wine lush, beer and cheese connoisseur