A pluggable field-handling system for the Behat Drupal Extension

Antonio De Marco
24 Mar 2015
4 Comments
Antonio De Marco
24 Mar 2015
4 Comments
Antonio De Marco, 24 Mar 2015 - 4 Comments

A pluggable field-handling system for the Behat Drupal Extension

Write more complete Behat test scenarios for both Drupal 7 and Drupal 8.

On of the main goal of BDD (Behaviour Driven Development) is to be able to describe a system's behavior using a single notation, in order to be directly accessible by product owners and developers and testable using automatic conversion tools.

In the PHP world, Behat is the tool of choice. Behat allows to write test scenarios using Gherkin step definitions and it generates the corresponding PHP code to actually run and test the defined scenarios.

Thanks to the excellent Behat Drupal Extension Drupal developers have been able to enjoy the benefits of Behavioral Driven Development for quite some time.

Essentially the project provides an integration between Drupal and Behat allowing the usage of Drupal-specific Gherkin step definitions. For example, writing a scenario that tests node authorship would look like:

Scenario: Create nodes with specific authorship
  Given users:
  | name     | mail            | status |
  | Joe User | joe@example.com | 1      |
  And "article" content:
  | title          | author   | body             |
  | Article by Joe | Joe User | PLACEHOLDER BODY |
  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"

Dealing with complex content types

The Gherkin scenario above is pretty straightforward and it gets the job done for simple cases. In a real-life situation, though, it's very common to have content types with a high number of fields, often of different types and, possibly, referencing other entities.

The following scenario might be a much more common situation for a Drupal developer:

Scenario: Reference site pages from within a "Post" node
  Given "page" content:
    | title      |
    | Page one   |
    | Page two   |
    | Page three |
  When I am viewing a "post" content:
    | title                | Post title         |
    | body                 | PLACEHOLDER BODY   |
    | field_post_reference | Page one, Page two |
  Then I should see "Page one"
  And I should see "Page two"

While it is always possible to implement project specific step-definition, as show on this Gist dealing with field collections and entity references, having to do that for every specific content type might be an unnecessary burden.

Introducing field-handling for the Behat Drupal Extension

Nuvole recently contributed a field-handling system that would allow the scenario above to be ran out of the box, without having to implement any custom step definition, working both in Drupal 7 and Drupal 8. The idea behind it is to allow a Drupal developer to work with fields when writing Behat test scenarios, regardless of the entity type or of any field-specific implementation.

The code is currently available on the master branches of both the Behat Drupal Extension and the Drupal Driver projects, if you want to try it out follow the instructions at "Stand-alone installation" and make sure to grab the right code by specifying the right package versions in your composer.json file:

{
  "require": {
    "drupal/drupal-extension": "3.0.*@dev",
    "drupal/drupal-driver": "1.1.*@dev"
}

The field-handling system provides an integration with several highly-used field types, like:

Date fields

Date field values can be included in a test scenario by using the following notation:

  • Single date field value can be expressed as 2015-02-08 17:45:00
  • Start and end date are separated by a dash -, for ex. 2015-02-08 17:45:00 - 2015-02-08 19:45:00.
  • Multiple date field values are separated by a comma ,

For example, the following Gherkin notation will create a node with 3 date fields:

When I am viewing a "post" content:
  | title       | Post title                                |
  | field_date1 | 2015-02-08 17:45:00                       |
  | field_date2 | 2015-02-08 17:45:00, 2015-02-09 17:45:00  |
  | field_date3 | 2015-02-08 17:45:00 - 2015-02-08 19:45:00 |

Entity reference fields

Entity reference field values can be expressed by simply specifying the referenced entity's label field (e.g. the node's title or the term's name). Such an approach wants to keep up with BDD's promise: i.e. describing the system behavior by abstracting, as much as possible, any internal implementation.

For example, to reference to a content item with title "Page one" we can simply write:

When I am viewing a "post" content:
  | title           | Post title |
  | field_reference | Page one   |

Or, in case of multiple fields, titles will be separated by a comma:

When I am viewing a "post" content:
  | title           | Post title         |
  | field_reference | Page one, Page two |

Link fields

A Link field in Drupal offers quite a wide range of options, such as an optional link title or internal/external URLs. We can use the following notation to work with links in our test scenarios:

When I am viewing a "post" content:
  | title       | Post title                                              |
  | field_link1 | http://nuvole.org                                       |
  | field_link2 | Link 1 - http://nuvole.org                              |
  | field_link3 | Link 1 - http://nuvole.org, Link 2 - http://example.com |

As you can see we use always the same pattern: a dash - to separate parts of the same field value and a comma , to separate multiple field values.

Text fields with "Select" widget

We can also refer to a select list value by simply referring to its label, so to have much more readable test scenarios. For example, given the following allowed values for a select field:

option1|Single room
option2|Twin room
option3|Double room

In our test scenario, we can simply write:

When I am viewing a "post" content:
  | title      | Post title               |
  | field_room | Single room, Double room |

Working with other entity types

Field-handling works with other entity types too, such as users and taxonomy terms. We can easily have a scenario that would create a bunch of users with their relative fields by writing:

Given users:
  | name    | mail                | language | field_name | field_surname | field_country  |
  | antonio | antonio@example.com | it       | Antonio    | De Marco      | Belgium        |
  | andrea  | andrea@example.com  | it       | Andrea     | Pescetti      | Italy          |
  | fabian  | fabian@example.com  | de       | Fabian     | Bircher       | Czech Republic |

Contributing to the project

At the moment field-handling is still a work in progress and, while it does support both Drupal 7 and Drupal 8, it covers only a limited set of field types, such as:

  • Simple text fields
  • Date fields
  • Entity reference fields
  • Link fields
  • List text fields
  • Taxonomy term reference fields

If you want to contribute to the project by providing additional field type handlers you will need to implement this very simple, core-agnostic, interface:

<?php
namespace Drupal\Driver\Fields;

/**
* Interface FieldHandlerInterface
* @package Drupal\Driver\Fields
*/
interface FieldHandlerInterface {

 
/**
   * Expand raw field values in a format compatible with entity_save().
   *
   * @param $values
   *    Raw field values array.
   * @return array
   *    Expanded field values array.
   */
 
public function expand($values);
}
?>

If you need some inspiration check the current handlers implementation by inspecting classes namespaced as \Drupal\Driver\Fields\Drupal7 and \Drupal\Driver\Fields\Drupal8.

Comments

Comments

Jesse Sutherland
25 Mar 2015

I've been wrangling w/ Behat off and on for the past year or so and never even knew about the extension. That is pretty darn nice work. Thanks!

I've been wrangling w/ Behat off and on for the past year or so and never even knew about the extension. That is pretty darn nice work. Thanks!

Jesse Sutherland, 25 Mar 2015

I've been wrangling w/ Behat off and on for the past year or so and never even knew about the extension. That is pretty darn nice work. Thanks!

Jesse Sutherland, 25 Mar 2015
fago
25 Mar 2015

Thanks for sharing, that's a great improvement.

I wonder whether you could further improve it with a reasonable default implementation for other field types based on the available metadata. E.g.: Link field could be "Link text: foo - URI: http://example.com, Link text: foo2 - URI: http://example2.com" based on the available labels of field item properties in Drupal 8. In Drupal 7 the Entity API module's wrapper & hook_entity_property_info() system gives you the same information for basically every field type.

Thanks for sharing, that's a great improvement.

I wonder whether you could further improve it with a reasonable default implementation for other field types based on the available metadata. E.g.: Link field could be "Link text: foo - URI: http://example.com, Link text: foo2 - URI: http://example2.com" based on the available labels of field item properties in Drupal 8. In Drupal 7 the Entity API module's wrapper & hook_entity_property_info() system gives you the same information for basically every field type.

fago, 25 Mar 2015

Thanks for sharing, that's a great improvement.

I wonder whether you could further improve it with a reasonable default implementation for other field types based on the available metadata. E.g.: Link field could be "Link text: foo - URI: http://example.com, Link text: foo2 - URI: http://example2.com" based on the available labels of field item properties in Drupal 8. In Drupal 7 the Entity API module's wrapper & hook_entity_property_info() system gives you the same information for basically every field type.

fago, 25 Mar 2015
Antonio De Marco
25 Mar 2015

That's actually a very good idea, I'll look into that, thanks!

That's actually a very good idea, I'll look into that, thanks!

Antonio De Marco, 25 Mar 2015

That's actually a very good idea, I'll look into that, thanks!

Antonio De Marco, 25 Mar 2015
Chris ZIetlow
2 Apr 2015

This is a huge step up from the former field mapping implementation from the drupal-extension 1.x days. I'm curious if this is can be overridden on a per project basis to allow for better flexibility.

For example (with 1.x) It provides a default method for creating users, but makes assumptions about what the user entities should look like. If you have an additional required field, then you'd be forced to create a custom step definition, which can be painful as Behat really doesn't like multiple definitions with matching annotations.

Again, fantastic work. Time for this dev to go dive into the new code.

This is a huge step up from the former field mapping implementation from the drupal-extension 1.x days. I'm curious if this is can be overridden on a per project basis to allow for better flexibility.

For example (with 1.x) It provides a default method for creating users, but makes assumptions about what the user entities should look like. If you have an additional required field, then you'd be forced to create a custom step definition, which can be painful as Behat really doesn't like multiple definitions with matching annotations.

Again, fantastic work. Time for this dev to go dive into the new code.

Chris ZIetlow, 2 Apr 2015

This is a huge step up from the former field mapping implementation from the drupal-extension 1.x days. I'm curious if this is can be overridden on a per project basis to allow for better flexibility.

For example (with 1.x) It provides a default method for creating users, but makes assumptions about what the user entities should look like. If you have an additional required field, then you'd be forced to create a custom step definition, which can be painful as Behat really doesn't like multiple definitions with matching annotations.

Again, fantastic work. Time for this dev to go dive into the new code.

Chris ZIetlow, 2 Apr 2015

Get your project started today!

Contact us