Welcome to the Drupal Extension to Behat and Mink’s documentation!¶
Contents:
Testing your site with the Drupal Extension to Behat and Mink¶
The Drupal Extension to Behat and Mink provides Drupal-specific functionality for the Behavior-Driven Development testing frameworks of Behat and Mink.
What do Behat and Mink Do?¶
Behat and Mink allow you to describe the behavior of a web site in plain, but stylized language, and then turn that description into an automated test that will visit the site and perform each step you describe. Such functional tests can help site builders ensure that the added value they’ve created when building a Drupal site continues to behave as expected after any sort of site change – security updates, new module versions, changes to custom code, etc.
What does the Drupal Extension add?¶
The Drupal Extension to Behat and Mink assists in the performance of these common Drupal testing tasks:
- Set up test data with Drush or the Drupal API
- Define theme regions and test data appears within them
- Clear the cache, log out, and other useful steps
- Detect and discover steps provided by contributed modules and themes
System Requirements¶
Meet the system requirements¶
Check your PHP version:
php --version
It must be higher than 5.3.5! Note: This means you cannot use the same version of PHP for testing that you might use to run a Drupal 5 site.
Check for Java:
java -version
It doesn’t necessarily matter what version, but it will be required for Selenium.
Directions are written to use command-line cURL. You can make sure it’s installed with:
curl --version
Selenium
Download the latest version of Selenium Server It’s under the heading Selenium Server (formerly the Selenium RC Server). This is a single file which can be placed any where you like on your system and run with the following command:
java -jar selenium-server-standalone-2.44.0.jar & // replace with the name of the version you downloaded
Stand-alone installation¶
A stand-alone installation is recommended when you want your tests and testing environment to be portable, from local development to CI server, to client infrastructure. It also makes documentation consistent and reliable.
Create a folder for your BDD tests:
mkdir projectfolder cd projectfolder
All the commands that follow are written to install from the root of your project folder.
Install Composer, a php package manager:
curl -s https://getcomposer.org/installer | php
Create a composer.json file to tell Composer what to install. To do that, paste the following code into your editor and save as composer.json. The Drupal Extension requires Behat, Mink, and the Mink Extension. They will all be set up because they’re dependencies of the Drupal Extension, so you don’t have to specify them directly in the composer.json file:
1 2 3 4 5 6 7 8 { "require": { "drupal/drupal-extension": "^3.2" }, "config": { "bin-dir": "bin/" } }
Run the following command to install the Drupal Extension and all those dependencies. This takes a while before you start to see output:
php composer.phar install
Configure your testing environment by creating a file called behat.yml with the following. Be sure that you point the base_url at the web site YOU intend to test. Do not include a trailing slash:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 default: suites: default: contexts: - FeatureContext - Drupal\DrupalExtension\Context\DrupalContext - Drupal\DrupalExtension\Context\MinkContext - Drupal\DrupalExtension\Context\MessageContext - Drupal\DrupalExtension\Context\DrushContext extensions: Drupal\MinkExtension: goutte: ~ selenium2: ~ base_url: http://seven.l Drupal\DrupalExtension: blackbox: ~
Initialize behat. This creates the features folder with some basic things to get you started, including your own FeatureContext.php file:
bin/behat --init
This will generate a FeatureContext.php file that looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php use Behat\Behat\Tester\Exception\PendingException; use Drupal\DrupalExtension\Context\RawDrupalContext; use Behat\Behat\Context\SnippetAcceptingContext; use Behat\Gherkin\Node\PyStringNode; use Behat\Gherkin\Node\TableNode; /** * Defines application features from the specific context. */ class FeatureContext extends RawDrupalContext implements SnippetAcceptingContext { /** * Initializes context. * * Every scenario gets its own context instance. * You can also pass arbitrary arguments to the * context constructor through behat.yml. */ public function __construct() { } }This FeatureContext.php will be aware of both the Drupal Extension and the Mink Extension, so you’ll be able to take advantage of their drivers add your own custom step definitions as well.
To ensure everything is set up appropriately, type:
bin/behat -dl
You’ll see a list of steps like the following, but longer, if you’ve installed everything successfully:
1 2 3 4 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
System-wide installation¶
A system-wide installation allows you to maintain a single copy of the testing tool set and use it for multiple test environments. Configuration is slightly more complex than the stand-alone installation but many people prefer the flexibility and ease-of-maintenance this setup provides.
Overview¶
To install the Drupal Extension globally:
- Install Composer
- Install the Drupal Extension in /opt/drupalextension
- Create an alias to the behat binary in /usr/local/bin
- Create your test folder
Install Composer¶
Composer is a PHP dependency manager that will make sure all the pieces you need get installed. Full directions for global installation and more information can be found on the Composer website.:
curl -sS https://getcomposer.org/installer |
php mv composer.phar /usr/local/bin/composer
Install the Drupal Extension¶
Make a directory in /opt (or wherever you choose) for the Drupal Extension:
cd /opt/ sudo mkdir drupalextension cd drupalextension/
- Create a file called composer.json and include the following:
1 2 3 4 5 6 7 8 { "require": { "drupal/drupal-extension": "^3.2" }, "config": { "bin-dir": "bin/" } }
Run the install command:
composer install
It will be a bit before you start seeing any output. It will also suggest that you install additional tools, but they’re not normally needed so you can safely ignore that message.
Test that your install worked by typing the following:
bin/behat --help
If you were successful, you’ll see the help output.
Make the binary available system-wide:
ln -s /opt/drupalextension/bin/behat /usr/local/bin/behat
Set up tests¶
- Create the directory that will hold your tests. There is no technical reason this needs to be inside the Drupal directory at all. It is best to keep them in the same version control repository so that the tests match the version of the site they are written for.
One clear pattern is to keep them in the sites folder as follows:
Single site: sites/default/behat-tests
Multi-site or named single site: /sites/my.domain.com/behat-tests
- Wherever you make your test folder, inside it create the behat.yml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 default: suites: default: contexts: - FeatureContext - Drupal\DrupalExtension\Context\DrupalContext - Drupal\DrupalExtension\Context\MinkContext - Drupal\DrupalExtension\Context\MessageContext - Drupal\DrupalExtension\Context\DrushContext extensions: Drupal\MinkExtension: goutte: ~ selenium2: ~ base_url: http://seven.l Drupal\DrupalExtension: blackbox: ~
Initialize behat. This creates the features folder with some basic things to get you started:
bin/behat --init
This will generate a FeatureContext.php file that looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php use Behat\Behat\Tester\Exception\PendingException; use Drupal\DrupalExtension\Context\RawDrupalContext; use Behat\Behat\Context\SnippetAcceptingContext; use Behat\Gherkin\Node\PyStringNode; use Behat\Gherkin\Node\TableNode; /** * Defines application features from the specific context. */ class FeatureContext extends RawDrupalContext implements SnippetAcceptingContext { /** * Initializes context. * * Every scenario gets its own context instance. * You can also pass arbitrary arguments to the * context constructor through behat.yml. */ public function __construct() { } }This will make your FeatureContext.php aware of both the Drupal Extension and the Mink Extension, so you’ll be able to take advantage of their drivers and step definitions and add your own custom step definitions here. The FeatureContext.php file must be in the same directory as your behat.yml file otherwise in step 5 you will get the following error:
[BehatBehatContextExceptionContextNotFoundException] FeatureContext context class not found and can not be used.
To ensure everything is set up appropriately, type:
behat -dl
You’ll see a list of steps like the following, but longer, if you’ve installed everything successfully:
1 2 3 4 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
Environment specific settings¶
Some of the settings in behat.yml
are environment specific. For example the
base URL may be http://mysite.localhost
on your local development
environment, while on a test server it might be http://127.0.0.1:8080
. Some
other environment specific settings are the Drupal root path and the paths to
search for subcontexts.
If you intend to run your tests on different environments these settings should
not be committed to behat.yml
. Instead they should be exported in an
environment variable. Before running tests Behat will check the BEHAT_PARAMS
environment variable and add these settings to the ones that are present in
behat.yml
. This variable should contain a JSON object with your settings.
Example JSON object:
{
"extensions": {
"Behat\\MinkExtension": {
"base_url": "http://myproject.localhost"
},
"Drupal\\DrupalExtension": {
"drupal": {
"drupal_root": "/var/www/myproject"
}
}
}
}
To export this into the BEHAT_PARAMS
environment variable, squash the JSON
object into a single line and surround with single quotes:
$ export BEHAT_PARAMS='{"extensions":{"Behat\\MinkExtension":{"base_url":"http://myproject.localhost"},"Drupal\\DrupalExtension":{"drupal":{"drupal_root":"/var/www/myproject"}}}}'
You must also remove (or comment out) the entries that you use in behat.yml for the values in BEHAT_PARAMS to take affect.
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: ~
# Must comment out for BEHAT_PARAMS to be effective.
# base_url: http://seven.l
Drupal\DrupalExtension:
# Anything used in BEHAT_PARAMS must be removed or commented.
# drupal:
# drupal_root: /var/www
# drush:
# alias: '@site'
blackbox: ~
# You can use profiles if you wish to allow users to run tests locally.
# Usage:
# bin/behat --profile=local
local:
extensions:
Behat\MinkExtension:
base_url: 'localhost'
Drupal\DrupalExtension:
drush:
alias: '@self'
drupal:
drupal_root: '../web'
There is also a Drush extension that can help you generate these environment variables.
Drupal Extension Drivers¶
The Drupal Extension provides drivers for interacting with your site which are compatible with Drupal 6, 7, and 8. Each driver has its own limitations.
Feature | Blackbox | Drush | Drupal API |
---|---|---|---|
Map Regions | Yes | Yes | Yes |
Create users | No | Yes | Yes |
Create nodes | No | Yes [*] | Yes |
Create vocabularies | No | Yes [*] | Yes |
Create taxonomy terms | No | Yes [*] | Yes |
Run tests and site on different servers | Yes | Yes | No |
[*] Requires that the Behat Drush Endpoint be installed on the Drupal site under test.
Blackbox Driver¶
The blackbox driver assumes no privileged access to the site. You can run the tests on a local or remote server, and all the actions will take place through the site’s user interface. This driver was enabled as part of the installation instructions by lines 13 and 14, highlighted below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
|
Region steps¶
It may be really important that a block is in the correct region, or you may have a link or button that doesn’t have a unique label. The blackbox driver allows you to create a map between a CSS selector and a user-readable region name so you can use steps like the following without having to write any custom PHP:
I press "Search" in the "header" region
I fill in "a value" for "a field" in the "content" region
I fill in "a field" with "Stuff" in the "header" region
I click "About us" in the "footer" region
Example:¶
A stock Drupal 7 installation has a footer area identified by the CSS Id “footer”. By editing the behat.yml file and adding lines 15 and 16 below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
region_map:
footer: "#footer"
|
You can use a step like the following without writing any custom PHP:
When I click "About us" in the "footer" region.
Using the blackbox driver configured with the regions of your site, you can access the following region-related steps:
Note
- These examples won’t work unless you define the appropriate regions in
- your behat.yml file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | Feature: Test DrupalContext
In order to prove the Drupal context using the blackbox driver is working properly
As a developer
I need to use the step definitions of this context
Scenario: Test the ability to find a heading in a region
Given I am on the homepage
When I click "Download & Extend"
Then I should see the heading "Core" in the "content" region
Scenario: Clicking content in a region
Given I am at "download"
When I click "About Distributions" in the "content" region
Then I should see "Page status" in the "right sidebar"
And I should see the link "Drupal News" in the "footer" region
Scenario: Viewing content in a region
Given I am on the homepage
Then I should see "Come for the software, stay for the community" in the "left header"
Scenario: Test ability to find text that should not appear in a region
Given I am on the homepage
Then I should not see the text "Proprietary software is cutting edge" in the "left header"
Scenario: Submit a form in a region
Given I am on the homepage
When I fill in "Search Drupal.org" with "Views" in the "right header" region
And I press "Search" in the "right header" region
Then I should see the text "Search again" in the "right sidebar" region
Scenario: Check a link should not exist in a region
Given I am on the homepage
Then I should not see the link "This link should never exist in a default Drupal install" in the "right header"
Scenario: Find a button
Given I am on the homepage
Then I should see the "Search" button
Scenario: Find a button in a region
Given I am on the homepage
Then I should see the "Search" button in the "right header"
Scenario: Find an element in a region
Given I am on the homepage
Then I should see the "h1" element in the "left header"
Scenario: Element not in region
Given I am on the homepage
Then I should not see the "h1" element in the "footer"
Scenario: Text not in element in region
Given I am on the homepage
Then I should not see "DotNetNuke" in the "h1" element in the "left header"
Scenario: Find an element with an attribute in a region
Given I am on the homepage
Then I should see the "h1" element with the "id" attribute set to "site-name" in the "left header" region
Scenario: Find text in an element with an attribute in a region
Given I am on the homepage
Then I should see "Drupal" in the "h1" element with the "id" attribute set to "site-name" in the "left header" region
|
Message selectors¶
The Drupal Extension makes use of three selectors for message. If your CSS values are different than the defaults (shown below), you’ll need to update your behat.yml file:
1 2 3 4 5 | Drupal\DrupalExtension:
selectors:
message_selector: '.messages'
error_message_selector: '.messages.messages-error'
success_message_selector: '.messages.messages-status'
|
Message-related steps include:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Scenario: Error messages
Given I am on "/user"
When I press "Log in"
Then I should see the error message "Password field is required"
And I should not see the error message "Sorry, unrecognized username or password"
And I should see the following error messages:
| error messages |
| Username field is required |
| Password field is required |
And I should not see the following error messages:
| error messages |
| Sorry, unrecognized username or password |
| Unable to send e-mail. Contact the site administrator if the problem persists |
Scenario: Messages
Given I am on "/user/register"
When I press "Create new account"
Then I should see the message "Username field is required"
But I should not see the message "Registration successful. You are now logged in"
|
Override text strings¶
The Drupal Extension relies on default text for certain steps. If you have customized the label visible to users, you can change that text as follows:
Drupal\DrupalExtension:
text:
log_out: "Sign out"
log_in: "Sign in"
password_field: "Enter your password"
username_field: "Nickname"
Drush Driver¶
Many tests require that a user logs into the site. With the blackbox driver, all user creation and login would have to take place via the user interface, which quickly becomes tedious and time consuming. You can use the Drush driver to add users, reset passwords, and log in by following the steps below, again, without having to write custom PHP. You can also do this with the Drupal API driver. The main advantage of the Drush driver is that it can work when your tests run on a different server than the site being tested.
Install Drush¶
See the Drush project page for installation directions.
Install the Behat Drush Endpoint¶
The Behat Drush Endpoint is a Drush-based service that the Drush Driver uses in order to create content on the Drupal site being tested. See the Behat Drush Endpoint project page for instructions on how to install it with your Drupal site.
Point Drush at your Drupal site¶
Drupal Alias (For local or remote sites)¶
You’ll need ssh-key access to a remote server to use Drush. If Drush and Drush aliases are new to you, see the Drush site for detailed examples
The alias for our example looks like:
1 2 3 4 5 6 7 8 9 | <?php
$aliases['local'] = array(
'root' => '/var/www/seven/drupal',
'uri' => 'seven.l'
);
$aliases['git7site'] = array(
'uri' => 'git7site.devdrupal.org',
'host' => 'git7site.devdrupal.org'
);
|
Path to Drupal (local sites only)¶
If you’ll only be running drush commands to access a site on the same machine, you can specify the path to your Drupal root:
1 2 3 4 | Drupal\DrupalExtension:
blackbox: ~
drush:
root: /my/path/to/drupal
|
Enable the Drush driver¶
In the behat.yml file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drush'
drush:
alias: 'local'
region_map:
footer: "#footer"
|
Note
Line 15 isn’t strictly necessary for the Drush driver, which is the default for the API.
Calling the Drush driver¶
Untagged tests use the blackbox driver. To invoke the Drush driver, tag the scenario with @api
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Feature: Drush alias
In order to demonstrate the Drush driver
As a trainer
I need to show how to tag scenarios
Scenario: Untagged scenario uses blackbox driver and fails
Given I am logged in as a user with the "authenticated user" role
When I click "My account"
Then I should see the heading "History"
@api
Scenario: Tagged scenario uses Drush driver and succeeds
Given I am logged in as a user with the "authenticated user" role
When I click "My account"
Then I should see the heading "History"
|
If you try to run a test without that tag, it will fail.
Example:¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | Feature: Drush alias
In order to demonstrate the Drush driver
As a trainer
I need to show how to tag scenarios
Scenario: Untagged scenario uses blackbox driver and fails
# features/drush.feature:6
Given I am logged in as a user with the "authenticated user" role
# FeatureContext::iAmLoggedInWithRole()
No ability to create users in Drupal\Driver\BlackboxDriver.
Put `@api` into your feature and add an api driver
(ex: `api_driver: drupal`) in behat.yml.
When I click "My account"
# FeatureContext::iClick()
Then I should see the heading "History"
# FeatureContext::assertHeading()
@api
Scenario: Tagged scenario uses Drush driver and succeeds
# features/drush.feature:12
Given I am logged in as a user with the "authenticated user" role
# FeatureContext::iAmLoggedInWithRole()
When I click "My account"
# FeatureContext::iClick()
|
The Drush driver gives you access to all the blackbox steps, plus those used in each of the following examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @api
Feature: Drush driver
In order to show functionality added by the Drush driver
As a trainer
I need to use the step definitions it supports
Scenario: Drush alias
Given I am logged in as a user with the "authenticated user" role
When I click "My account"
Then I should see the heading "History"
Scenario: Target links within table rows
Given I am logged in as a user with the "administrator" role
When I am at "admin/structure/types"
And I click "manage fields" in the "Article" row
Then I should be on "admin/structure/types/manage/article/fields"
And I should see text matching "Add new field"
Scenario: Clear cache
Given the cache has been cleared
When I am on the homepage
Then I should get a "200" HTTP response
|
If the Behat Drush Endpoint is installed on the Drupal site being tested, then you will also have access to all of the examples shown for the Drupal API driver.
Drupal API Driver¶
The Drupal API Driver is the fastest and the most powerful of the three drivers. Its biggest limitation is that the tests must run on the same server as the Drupal site.
Enable the Drupal API Driver¶
To enable the Drupal API driver, edit the behat.yml file, change the api_driver to drupal and add the path to the local Drupal installation as shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drupal'
drush:
alias: 'local'
drupal:
drupal_root: '/var/www/seven/drupal'
region_map:
footer: "#footer"
|
Note
It’s fine to leave the information for the drush driver in the file. It’s the api_driver value that declares which setting will be used for scenarios tagged @api.
Using this driver, you gain the ability to use all the steps in the examples below (and more).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | @api
Scenario: Create a node
Given I am logged in as a user with the "administrator" role
When I am viewing an "article" content with the title "My article"
Then I should see the heading "My article"
Scenario: Run cron
Given I am logged in as a user with the "administrator" role
When I run cron
And am on "admin/reports/dblog"
Then I should see the link "Cron run completed"
Scenario: Create many nodes
Given "page" content:
| title |
| Page one |
| Page two |
And "article" content:
| title |
| First article |
| Second article |
And I am logged in as a user with the "administrator" role
When I go to "admin/content"
Then I should see "Page one"
And I should see "Page two"
And I should see "First article"
And I should see "Second article"
Scenario: Create nodes with fields
Given "article" content:
| title | promote | body |
| First article with fields | 1 | PLACEHOLDER BODY |
When I am on the homepage
And follow "First article with fields"
Then I should see the text "PLACEHOLDER BODY"
Scenario: Create and view a node with fields
Given I am viewing an "Article" content:
| title | My article with fields! |
| body | A placeholder |
Then I should see the heading "My article with fields!"
And I should see the text "A placeholder"
Scenario: Create users
Given users:
| name | mail | status |
| Joe User | joe@example.com | 1 |
And I am logged in as a user with the "administrator" role
When I visit "admin/people"
Then I should see the link "Joe User"
Scenario: Login as a user created during this scenario
Given users:
| name | status |
| Test user | 1 |
When I am logged in as "Test user"
Then I should see the link "Log out"
Scenario: Create a term
Given I am logged in as a user with the "administrator" role
When I am viewing a "tags" term with the name "My tag"
Then I should see the heading "My tag"
Scenario: Create many terms
Given "tags" terms:
| name |
| Tag one |
| Tag two |
And I am logged in as a user with the "administrator" role
When I go to "admin/structure/taxonomy/tags"
Then I should see "Tag one"
And I should see "Tag two"
Scenario: Create nodes with specific authorship
Given users:
| name | mail | status |
| Joe User | joe@example.com | 1 |
And "article" content:
| title | author | body | promote |
| Article by Joe | Joe User | PLACEHOLDER BODY | 1 |
When I am logged in as a user with the "administrator" role
And I am on the homepage
And I follow "Article by Joe"
Then I should see the link "Joe User"
Scenario: Create an article with multiple term references
Given "tags" terms:
| name |
| Tag one |
| Tag two |
| Tag three |
| Tag four |
And "article" content:
| title | field_tags |
| My first article | Tag one |
| My second article | Tag two, Tag three |
| My third article | Tag two, Tag three, Tag four |
|
Contexts¶
Before Behat 3, each test suite was limited to a single context class. As of Behat 3, it is possible to flexibly structure your code by using multiple contexts in a single test suite.
Available Contexts¶
In accordance with this new capability, The Drupal Extension includes the following contexts:
- RawDrupalContext
- A context that provides no step definitions, but all of the necessary functionality for interacting with Drupal, and with the browser via Mink sessions.
- DrupalContext
- Provides step-definitions for creating users, terms, and nodes.
- MinkContext
- Builds on top of the Mink Extension and adds steps specific to regions and forms.
- MarkupContext
- Contains step definitions that deal with low-level markup (such as tags, classes, and attributes).
- MessageContext
- Step-definitions that are specific to Drupal messages that get displayed (notice, warning, and error).
- RandomContext
- Contains transforms that allow for use of placeholders such as <?title> that will be replaced with a random string when the scenario is run: Given I am viewing an “Article” with the title “<?title>”
- DrushContext
- Allows steps to directly call drush commands.
- BatchContext
- Steps for creating batch items and ensuring a batch is finished processing.
Custom Contexts¶
You can structure your own code with additional contexts. See Behat’s testing features documentation for a detailed discussion of how contexts work.
Important
Every context you want to use in a suite must be declared in the behat.yml file.
Example¶
In this example, you would have access to:
- pre-written step definitions for users, terms, and nodes (from the
DrupalContext
)- steps you’ve implemented in the main
features/bootstrap/FeatureContext.php
file- steps you’ve implemented in the
CustomContext
class
You would not have access to the steps from the MarkupContext
,
MessageContext
, or DrushContext
, however.
1 2 3 4 5 6 7 | default:
suites:
default:
contexts:
- Drupal\DrupalExtension\Context\DrupalContext
- FeatureContext
- CustomContext
|
Context communication¶
Since Behat 3 can have many concurrent contexts active, communication between those contexts can be important.
The following will gather any specified contexts before a given scenario is run:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php // Snippet to demonstrate context communications. /** * Gather any contexts needed. * * @BeforeScenario */ public function gatherContexts(BeforeScenarioScope $scope) { $environment = $scope->getEnvironment(); $this->drupalContext = $environment->getContext('Drupal\DrupalExtension\Context\DrupalContext'); $this->minkContext = $environment->getContext('Drupal\DrupalExtension\Context\MinkContext'); }
Drupal Extension Hooks¶
In addition to the hooks provided by Behat, the Drupal
Extension provides three additional ways to tag the methods in your
CustomContext
class in order to have them fire before certain events.
@beforeNodeCreate
@beforeTermCreate
@beforeUserCreate
Example¶
1 2 3 4 5 6 7 8 9 10 11 | use Drupal\DrupalExtension\Hook\Scope\EntityScope;
...
/**
* Call this function before nodes are created.
*
* @beforeNodeCreate
*/
public function alterNodeObject(EntityScope $scope) {
$node = $scope->getEntity();
// Alter node object as needed.
}
|
Contributed Module Subcontexts¶
Although not yet a wide-spread practice, the Drupal Extension to Behat and Mink makes it easy for maintainers to include custom step definitions in their contributed projects.
Discovering SubContexts¶
In order to use contributed step definitions, define the search path in the behat.yml
// sites/default/behat-tests/behat.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
paths:
- "./path_to_module/features"
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drupal'
drush:
alias: 'local'
drupal:
drupal_root: '/var/www/seven/drupal'
region_map:
footer: "#footer"
subcontexts:
paths:
- "/var/www/seven/drupal/sites/all"
|
The Drupal Extension will search recursively within the directory or directories specified to discover and load any file ending in .behat.inc. This system, although created with Drupal contrib projects in mind, searches where it’s pointed, so you can also use it for your own subcontexts, a strategy you might employ to re-use step definitions particular to your shop or company’s development patterns. The paths key allows running tests located in features within the features directory of a contributed/custom module.
Disable autoloading¶
Autoloading can be disabled in the behat.yml file temporarily with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | default:
suites:
default:
contexts:
- FeatureContext
- Drupal\DrupalExtension\Context\DrupalContext
- Drupal\DrupalExtension\Context\MinkContext
extensions:
Drupal\MinkExtension:
goutte: ~
selenium2: ~
base_url: http://seven.l
Drupal\DrupalExtension:
blackbox: ~
api_driver: 'drupal'
drush:
alias: 'local'
drupal:
drupal_root: '/var/www/seven/drupal'
region_map:
footer: "#footer"
subcontexts:
paths:
- "/var/www/seven/drupal/sites/all"
autoload: 0
|
For Contributors¶
Behat subcontexts are no longer supported in version 3. The Drupal Extension, however, continues to support saving module-specific contexts in a file ending with .behat.inc
Just like functions, preface the filename with the project’s machine name to prevent namespace collisions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <?php /** * Contains \FooFoo. */ use Behat\Behat\Hook\Scope\BeforeScenarioScope; use Behat\Behat\Tester\Exception\PendingException; use Drupal\DrupalExtension\Context\DrupalSubContextBase; use Drupal\DrupalExtension\Context\DrupalSubContextInterface; /** * Example subcontext. */ class FooFoo extends DrupalSubContextBase implements DrupalSubContextInterface { /** * @var \Drupal\DrupalExtension\Context\DrupalContext */ protected $drupalContext; /** * @var \Drupal\DrupalExtension\Context\MinkContext */ protected $minkContext; /** * @BeforeScenario */ public function gatherContexts(BeforeScenarioScope $scope) { $environment = $scope->getEnvironment(); $this->drupalContext = $environment->getContext('Drupal\DrupalExtension\Context\DrupalContext'); $this->minkContext = $environment->getContext('Drupal\DrupalExtension\Context\MinkContext'); } /** * @Given I create a(an) :arg1 content type */ public function CreateAContentType($arg1) { $this->minkContext->assertAtPath("admin/structure/types/add"); $node = [ 'title' => 'Test content!', ]; $this->drupalContext->nodeCreate($node); } /** * @Then /^I should have a subcontext definition$/ */ public function assertSubContextDefinition() { throw new PendingException(); } }