User login

Getting started with Behat testing for Drupal

Behat is a tool to support behavior-driven development in PHP. Behavior-driven development, in turn, is a set of practices and tools to bring leadership, technical, and other participants in a software project to the same understanding of needed outcomes. The key to this is to describe what people should be able to do with the application in language that all participants understand. The role of tools like Behat is to enable these human-readable sentences to be run as machine-readable tests.

To recap: Behat lets everyone decide on the same straightforward statements about how a software application should work and then use those statements in automated testing.

  @drush
  Scenario: Create and log in as a user
    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 these steps have been defined already — which they are by the Behat Drupal Extension project — then we can run behat and watch it highlight each of these steps in green.

Behat isn't magic. The Drupal Extension project needed to have already defined for us the steps I am logged in as a user with the "<role>" role, I click "<link>", and I see the heading "<heading>". But once the steps to a broad set of common actions are defined such that sentences can be run as automated tests, it sure feels a lot like magic.

(Noticed the @drush notation before the scenario? That indicates to Behat that Drupal Extensions Drush API must be used, in this case for creating the user.)

Installing Behat and Drupal Extension

We use https://www.drupal.org/project/drupalextension and for a per-project installation those instructions are all that you need for Behat Drupal Extension, Behat itself, and its dependencies (thanks to Composer).

You don't need to install Behat in your project root; a global installation is possible or just make a subfolder 'behat' and go into it (mkdir behat && cd behat and then follow Steps 1 - 4, for 3 for the behat.yml file change the base_url to that of your site and (their step 6) add beneath "blackbox: ~" (at the same level of indent) the path to your Drupal web root:

  drush:
    root: /my/path/to/drupal

(Where /my/path/to/drupal is actually your path; for instance on Agaric's virtual machines that's /vagrant/drupal.)

The other steps from the Drupal Extension project page (and its README.md) are all useful but can be revisited later.

Now try out their step 5,

bin/behat -dl

And you should see a long list of available steps like:

Given /^(?:that I|I) am at "(?P[^"]*)"$/
When /^I visit "(?P[^"]*)"$/
When /^I click "(?P<link>[^"]*)"$/

These are the statements available for use in scenarios which can be tested by Behat.

Add a Feature (one or more Scenarios)

Here's an example of one feature to get you started. (Note that the feature statement is optional, and you can jump straight to scenarios in your .feature file.)

In the 'features' folder, create a file named site_builder.feature.

Feature: Site building
  In order to add features to my Drupal site
  As a site builder
  I need to be able to administer content types

  @drush
  Scenario: Reach the manage fields page of the article content type
    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"

This scenario was blatantly ripped off from DrupalExtension's own .feature files for testing itself, and that's a fine place to look for how the list of steps get applied in scenarios. Look in the folder where you installed behat for vendor/drupal/drupal-extension/features — blackbox.feature are tests against drupal.org that can be run without having to log in; api.feature has scenarios that require log-in.

We're still looking for the giant cache of scenarios common to Drupal sites for everyone to use and remix into scenarios / tests, but in the meantime using this style and the list of available steps (from behat -dl), you'll be able to write your own. The Behat documentation has much more on scenario-writing practice and theory.

Finding available steps

The long list of steps from behat -dl may not be the most convenient when looking for a particular one. Use grep to filter them down to what you're interested in. For instance behat -dl | grep user will show user-related statements, like so:

vagrant@packer-virtualbox-iso:/vagrant/behat$ bin/behat -dl | grep user
Given /^I am an anonymous user$/
Given /^I am logged in as a user with the "(?P<role>[^"]*)" role$/
Given /^I am logged in as a user with the "(?P<permission>[^"]*)" permission(?:|s)$/
Given /^users:$/

That last one is particularly interesting because it is actually how you have users created before beginning a scenario, without already being logged in as that user. Like most of what Drupal Extension provides the function defining these statements can be found in vendor/drupal/drupal-extension/src/Drupal/DrupalExtension/Context/DrupalContext.php

The code is reproduced in full, but the important part is

<?php
 
/**
   * Creates multiple users.
   *
   * Provide user data in the following format:
   *
   * | name     | mail         | roles        |
   * | user foo | <a href="mailto:foo@bar.com">foo@bar.com</a>  | role1, role2 |
   *
   * @Given /^users:$/
   */
 
public function createUsers(TableNode $usersTable) {
    foreach (
$usersTable->getHash() as $userHash) {

     

// If we have roles convert it to array.
     
if (isset($userHash['roles'])) {
       
$userHash['roles'] = explode(',', $userHash['roles']);
       
$userHash['roles'] = array_map('trim', $userHash['roles']);
      }

     

$user = (object) $userHash;

     

// Set a password.
     
if (!isset($user->pass)) {
       
$user->pass = $this->getDrupal()->random->name();
      }

     

$this->dispatcher->dispatch('beforeUserCreate', new EntityEvent($this, $user));
     
$this->getDriver()->userCreate($user);
     
$this->dispatcher->dispatch('afterUserCreate', new EntityEvent($this, $user));

     

$this->users[$user->name] = $user;
    }
  }
?>

You shouldn't have to find it in the code to learn how to use a Behat step, but for the format which commands like this take it seems to be necessary.

behat -h

will tell you that

behat -di

will give you extended definitions.

And grep for narrowing down isn't needed:

behat -d 'example'

will search step definitions for the string example, and only return the matches.

But to see the extended definitions, filtered, seems you still need grep:

behat -di | grep "users"

But as the other part of the definition "Provide user data in the following format" with the pipe-separated table is only in the code, it seems.

Evaluating errors

If you run behat and see something like this:

<?php
scenarios
(2 undefined)
12 steps (10 skipped, 2 undefined)
0m0.216s

You can implement step definitions

for undefined steps with these snippets:

 

/**
   * @Given /^there are following users:$/
   */
 
public function thereAreFollowingUsers(TableNode $table) {
    throw new
PendingException();
  }
?>

Then you made a typo or a missed word in the step definition you are attempting to use, the step definition you are attempting to use is not installed locally or correctly found by behat, or the step definition you are attempting to use has not been written anywhere by anyone, and if you must use it, you'll have to write it yourself, or get someone to write it for you.

Links to much more information

Drupal Extension - integration layer between Behat, Mink Extension, and Drupal.

Provides step definitions for common testing scenarios specific to Drupal.

https://www.drupal.org/project/drupalextension
http://dspeak.com/drupalextension/globalinstall.html
https://packagist.org/packages/drupal/drupal-extension
http://dspeak.com/pnwds/0-foundation.html

Working link to Mink: https://github.com/Behat/en-mink.behat.org/blob/master/index.rst

Extending Drupal Extension...
http://www.previousnext.com.au/blog/custom-step-definitions-drupal-drupalextension-and-behat

http://www.previousnext.com.au/blog/advanced-testing-drupal-emails-behat-and-testingmailsystem

Video:
https://www.youtube.com/watch?v=b_35hrRSVog
Slide deck - Drupal, Behat and Selenium
https://speakerdeck.com/weaverryan/behavior-driven-with-behat-and-drupal


Behat Runner — https://www.drupal.org/project/behatrunner — is the way of Drupal 8 (proposed and being worked on), backported to Drupal 7.


http://www.metaltoad.com/blog/what-i-learned-today-drupal-behat-scenario-cleanup
http://affinitybridge.com/blog/testing-drupal-distributions-using-behat-mink-drupal-extension-and-travis-ci
https://www.opensourcery.com/blog/sheldon-kreger/using-drupal-taxonomy-terms-behat-tests


https://groups.drupal.org/behat
https://www.drupal.org/project/behat_testing (an actual module and....)
https://www.drupal.org/files/project-images/beehat-drupalicon.png

Searched words: 
behat mink extensions features scenarios testing user stories

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.