AUTOMATION TESTING

An automation  framework built around Behat for testing Drupal sites

 

Brendan MacDonald

Available on Github

https://github.com/cameronandwilding/CWTest_Behat

 

WHY did we make this framework?

  • reusable
  • generic
  • simple to deploy
  • automatic automation
  • make testing faster
  • incorporate into the CI process
  • encourage team-wide testing
  • enforce a standard for our tests
  • future re-work of a site

WHAT's included In the framework?

  • Designed to sit in your drupal project codebase
  • Composer installation
  • Templates for testing content types:
    • .features
    • contexts
    • page objects
  • Page Object classes approach
  • Lots of custom functions inside a helper context
  • Scripts to perform various jobs
    • execution
    • controlling drivers
  • Reporting & archiving of results

 

COMPOSER installation

{
  "name": "cw/behat_test",
  "authors": [
    {
      "name": "Cameron and Wilding",
      "email": "info@cameronandwilding.com"
    }
  ],
  "require": {
    "behat/behat": "3.0.6",
    "drupal/drupal-extension": "3.1.5",
    "emuse/behat-html-formatter": "0.1.0",
    "phpunit/phpunit": "*"
  },
  "autoload": {
    "psr-4": {
      "CWTest\\": "src/"
    }
  },
  "bin": [
    "bin/start_phantomjs_webdriver.sh",
    "bin/start_selenium_server.sh",
    "bin/stop_phantomjs_webdriver.sh",
    "bin/stop_selenium_server.sh",
    "bin/cwtest-bootstrap.sh"
  ]
}

Feature Context

Feature file with a 'Create Article' Scenario

Typical behat approach

@article 
Scenario: Create an Article
Given I am on '/node/add/article'
    And I fill in 'edit-title-0-value' with 'Article Title'
When I press 'edit-submit'
Then I should see 'Article Article Title has been created.' in the '.messages--status'
/**
 * Fills in form field with specified id|name|label|value
 */
public function fillField($field, $value)
{
    $field = $this->fixStepArgument($field);
    $value = $this->fixStepArgument($value);
    $this->getSession()->getPage()->fillField($field, $value);
}

'Create Article' scenario

Typical login scenario

@article 
Scenario: Create an Article
Given I am on '/node/add/article'
    And I fill in 'edit-title-0-value' with 'Article Title'
When I press 'edit-submit'
Then I should see 'Article Article Title has been created.' in the '.messages--status'

Framework behat approach

@article
Scenario: Create an Article 
Given I am logged in as a user with the administrator role
    And I visit the Create Article page
When I enter the following values on the Create Article page
  | FIELD | VALUE                                 |
  | TITLE | Article Title <alpha_number>          |
  | BODY  | This is the body text of the Article. |
  | IMAGE | 150x350.jpg                           |
  | ALT   | ALT - 150x350.jpg                     |
    And I press save and publish
Then I verify that the article was created successfully

'Create Article' scenario

@article
Scenario: Create an Article 
Given I am logged in as a user with the administrator role
    And I visit the Create Article page
...
<?php

use CWTest\Util\ArticlePage;

class ArticleContext extends PageContext  {

...
<?php

class ArticlePage extends Page {

...

Article Context

Article Page

'Create Article' Scenario

framework article scenario

Article PAGE OBJECTS

class ArticlePage {

  /**
   * The path to the Article Content Type.
   *
   * @var string
   */
  private $path = '/node/add/article';

  /**
   * Fields visble in Create and Edit mode.
   *
   * @var array
   */
  private $fields = array(
    'TITLE' => 'edit-title-0-value',
    'IMAGE' => 'edit-field-image-0-upload',
  );

  /**
   * Frames available in Create and Edit mode.
   *
   * @var array
   */
  private $frames = array(
    'BODY' => 'cke_edit-body-0-value'
  );

  /**
   * Buttons visible in Create mode.
   *
   * @var array
   */
  private $create_buttons = array(
    'SAVE_AND_PUBLISH' => 'Save and publish',
    'SAVE_AS_UNPUBLISHED' => 'Save as unpublished',
    'PREVIEW' => 'Preview',
  );

  /**
   * Buttons visible in Edit mode.
   *
   * @var array
   */
  private $edit_buttons = array(
    'REMOVE' => 'Remove',
    'SAVE_AND_KEEP_PUBLISHED' => 'Save and keep published',
    'SAVE_AND_UNPUBLISH' => 'Save and unpublish',
    'PREVIEW' => 'Preview',
  );

article PAGE CONTEXT

class ArticleContext extends PageContext {

  /**
   * ArticlePage instance.
   *
   * @var ArticlePage
   */
  private $articlePage;

  /**
   * ArticleContext constructor.
   */
  public function __construct() {
    parent::__construct();
    $this->articlePage = new ArticlePage();
  }

   /**
   * @Given I visit the Create Article page
   */
  public function visitCreateArticlePage() {
    $this->helperContext->visitPath($this->articlePage->getPath());
  }

  /**
   * @Given I visit the Edit Article page
   */
  public function visitEditArticlePage() {
    $this->helperContext->visitPath(self::getEditPath());
  }

complete article title EXAMPLE

  /**
   * Fills in the title field.
   *
   * @param string $title
   */
  private function fillTitleField($title) {
    $this->helperContext->iFillInFieldByIDWith(
        $this->articlePage->getField(self::FIELD_TITLE), $title);
  }
  /**
   * @Given I enter the following values on the Create Article page
   */
  public function iEnterTheFollowingValuesOnTheCreateArticlePage(TableNode $table) {
    foreach ($table->getHash() as $key => $value) {
      $field = trim($value['FIELD']);
      $value = trim($value['VALUE']);

      switch ($field) {
        case self::FIELD_TITLE:
          self::fillTitleField($value);
          break;
    ...

Templates

Feature files for content type tests:

  • validation rules
  • page structure
  • create, edit, delete, & view

Page object class:

  • paths
  • array for each object type
  • getter functions

Context class:

  • perform actions on the content type

Templates

helper context

<?php
/**
 * @file
 */

namespace CWTest\Context;

use Drupal\DrupalExtension\Context\RawDrupalContext;
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
use Behat\Behat\Hook\Scope\AfterStepScope;
use Drupal\DrupalExtension\Context\MinkContext;
use Behat\Gherkin\Node\TableNode;
use Behat\Behat\Context\SnippetAcceptingContext;
use CWTest\Exception\CWContextException;
use CWTest\Util\RandomItems;

/**
 * Class HelperContext
 *
 * HelperContext contains supporting functions for all Behat projects.
 */
class HelperContext extends RawDrupalContext implements SnippetAcceptingContext {

...

BEHAT SCRIPT RUNNER

./run-behat.sh [tag] [profile]
./run-behat.sh article phantomjs

Script runner syntax

To run all your article tests on firefox:

To run all your article tests using phantomsjs:

./run-behat.sh article firefox

To run a specific test within the article tests on firefox:

./run-behat.sh article firefox "Verify the structure of the Create Article page"

Supporting scripts

Installation script:

  • downloads selenium server
  • creates directory structure for the tests and reporting
  • copies template files

 

Tests can be run using selenium or phantomjs.

  • the framework includes scripts for stopping and starting both
  • these scripts are automatically triggered by the behat runner script

Reporting

HTML reports:

  • Twig
  • Behat2

 

Results folder contains:

  • HTML reports
  • HTML dumps
  • Screenshots

 

Reporting

Reporting

configuration

Configuration split between 2 YML files:

 

behat.yml

 

 

default:
  suites:
    login:
      paths: [ %paths.base%/features ]
      filters:
        tags: @login
      contexts:
        - CWTest\Context\LoginContext
        - CWTest\Context\MyAccountContext
        - CWTest\Context\PageContext
        - CWTest\Context\HelperContext:
            parameters:
              screenshot_path: %paths.base%/../Results/Behat/screenshots
        - Drupal\DrupalExtension\Context\MinkContext
        - Drupal\DrupalExtension\Context\DrupalContext
        ...

configuration

Configuration split between 2 YML files:

 

behat.local.yml

 

 

default:
  extensions:
    Behat\MinkExtension:
      base_url: 
    Drupal\DrupalExtension:
      drupal:
        drupal_root: 

DEMO

incorporating feedback

Multiple versions/attempts at the framework.

 

Behat knowledge is a pre-requisite.

 

Composer set-up was not ideal.

 

Reporting was inadequate.

 

 

Conclusion

Future ideas!

 

Try it out and let us know what you think.

 

https://github.com/cameronandwilding/CWTest_Behat

 

 

 

Automation Framework build around Behat for testing Drupal sites

By Brendan MacDonald

Automation Framework build around Behat for testing Drupal sites

  • 1,145