A First look at Behat Testing in Drupal – Why should you bother? – Why your client should care about testing



A First look at Behat Testing in Drupal – Why should you bother? – Why your client should care about testing

0 0


behat

Intro to Behat in Drupal

On Github aczietlow / behat

A First look at Behat Testing in Drupal

Chris Zietlow \ @aczietlow

Why should you bother?

  • Better code quality
  • Less Regression
  • Better Acceptance Criteria
  • Greater understanding of code base
  • Faster development

What is quality anyway?

your team should decide what the code quality means to you.

  • Testible
  • Compliant
  • Clear
  • Well Documented
One does not simply add quality later

Faster development you say?

  • Fewer bugs, less time squashing them
  • Forces you to better understand code base
  • Avoid building code on top of an unknown bug

Why your client should care about testing

  • Better product quality
  • Less Regression
  • Better Acceptance Criteria
  • Faster development
  • Better product quality - better functioning product
  • Less Regression - Less time going back t fix bugs
  • Better Acceptance Criteria - Product better fits actually business needs
  • Greater understanding of product -
  • Faster development - more bang for your buck

Why isn't everyone doing this testing thing?

Testing takes time

Case studies conducted at Microsoft and IBM indicate that teams that adopt TDD experience 40% to 90% bugs, which results in 15-35% additional development time.

More Excuses

Writing good tests is hard

"Imperfect tests, run frequently, are much better than perfect tests that are never written at all." - Martin Fowler

Enter Behat: Our hero

Behat is a BDD (Behavior Driven Development) framework written in PHP

Seriously, we can do this in Drupal?

There's the module for that, because of course there is

Drupal Extension

Installation

Composer.json

"require": {
        "behat/behat": "3.0.*@stable",
        "drupal/drupal-extension": "3.0.*@dev"
    }

Behat.yml

default:
    suites:
        default:
            contexts:
                - FeatureContext
                - Drupal\DrupalExtension\Context\DrupalContext
extensions:
    Behat\MinkExtension:
Use your google powers

User Login

Client Requirement

I want to be able to log into my website in order to edit my content.

User Login

Requirements we can use

As a an anonymous user,
I want to be able to log into my website,
So that I can edit my content.
3 lines that describe the role, the feature, and the benefit

User Login

Acceptance Criteria

What does it mean to be logged in? How do we know that we are logged in?

Given I am an anonymous user
When I am at "user/login"
And I fill in "name" with "admin"
And I fill in "pass" with "admin"
And press "Log in"
Then I should see the link "Log out"
Given [some context] When [some event] Then [outcome]

User Login

Acceptance Criteria

How do we check can edit content.

Given I am logged in as a user with the administrator role
When I am at "node/add/page"
Then I should see "Create Basic page"
And I should get a 200 HTTP response

User Login

Slap it in a feature file and boom you have your test.

#./features/user_login.feature
@api
Feature: User Login.
  As a an anonymous user,
  I want to be able to log into my website,
  So that I can edit my content.

Scenario: I can log in.
  Given I am an anonymous user
  When I am at "user/login"
  And I fill in "name" with "admin"
  And I fill in "pass" with "admin"
  And press "Log in"
  Then I should see the link "Log out"

Scenario: I can edit content
  Given I am logged in as a user with the administrator role
  When I am at "node/add/page"
  Then I should see "Create Basic page"
  And I should get a 200 HTTP response
  • Top Section: Describe the feature. Provides human context
  • Each feature is described by multiple scenarios - How the feature should act under certain conditions
  • Each of the scenario lines map to function that defines what actions to take.

Define Features

Tests are saved in .feature files

Executing tests

$ bin/behat features/user_login.feature

Creating Content

@api
Feature: Basic page nodes
  As a admin user
  I want to have the ability to create basic page nodes
  So that I can populate the site with content.

Scenario: I have permission to create a basic page
  Given I am logged in as a user with the administrator role
  Then I should see the text "Log out"
  When I visit "node/add/page"
  Then I should get a 200 HTTP response
  And I should see "Create Basic page"

Scenario: Newly created page nodes appear on the homepage.
  Given "page" content:
  | title | body | author |
  | Page Content | This is my body field | admin |
  When I go to the homepage
  Then I should see "Page Content"

Events Feature

@api
Feature: Content Type Event
    As an anonymous user
    I want to view data related to an Event
    So that I can decide it is something I would like to attend.

Scenario: Add an event node and verify that the necessary fields exist.
    Given I am an anonymous user
    And an "Event Terms" term with the name "camp"
    And an "Event Terms" term with the name "drupal"
    And "event" content:
    | title | field_event_location | field_event_date | field_event_tags | status |
    | Atlanta Drupal Camp | Atlanta | 2015-04-11 9:00:00 | camp, drupal | 1 |
    When I am at "content/atlanta-drupal-camp"
    Then I should see the text "Atlanta Drupal Camp"
    And I should see the text "Atlanta"
    And I should see the text "Saturday, April 11, 2015"
    And I should see the text "camp"
    And I should see the text "drupal"

Behat Scenario Clean Up

Tests should return the site back to the state it was in before the test run.

/**
 * Remove any created nodes.
 *
 * @AfterScenario
 */
public function cleanNodes() {
  // Remove any nodes that were created.
  foreach ($this->nodes as $node) {
    $this->getDriver()->nodeDelete($node);
  }
}

Step Defintions

Each scenario step maps to a function that performs the step

Given I am an anonymous user
/**
 * @Given I am an anonymous user
 * @Given I am not logged in
 */
 public function assertAnonymousUser() {
   // Verify the user is logged out.
   if ($this->loggedIn()) {
   $this->logout();
 }

Step Definitions

Out of the box the Mink and Drupal Drivers will provide a lot of step definitions that you can use.

$ behat -dl
default | When I visit the login page
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 a user with the :role role(s) and I have the following fields:
default | Given I am logged in as :name
default | Given I am logged in as a user with the :permissions permission(s)
default | Then I should see (the text ):text in the ":rowText" row
default | Given I click :link in the :rowText row

Custom Step Definitions

Feature: Migrations
    As an editor of the website
    I want the workbench module enabled
    So that I can create custom editing work flows


    Scenario: Confirm the workbench module is installed
        Given the "workbench" module is installed

Custom Step Definitions

Custom step definitions go in features/bootstrap/FeatureContext.php

//features/bootstrap/FeatureContext.php
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
{

}

Custom Step Defintions

/**
 * Asserts that a given module exists and is enabled.
 *
 * @Given the :module module is installed
 */
 public function assertModuleExists($module)
 {
   if (module_exists($module)) {
   return TRUE;
 }

  $message = sprintf('Module "%s" is not installed.', $module);
  throw new \Exception($message);
}
By extending the drupal extension we get access to a bootstrapped drupal as well as any methods they've already defined, like nodeCreate() and login()

More custom defintions

Then "events" migration is complete$/
/**
 * Asserts the given migration is complete.
 *
 * @Then /^the "([^"]*)" migration is complete$/
 *
 * @throws /Exception if the migration did not complete.
 */
 public function assertMigrationIsComplete($name)
 {
   $migration = Migration::getInstance($name);
   if ($migration->isComplete()) {
     return TRUE;
   }
   $message = sprintf('The "%s" migration is not complete.', $name);
   throw new \Exception($message);
}

Doing things in the browser

We use Mink to interact with the browser

Some common usages of the Mink API

$element = $session->getPage();

$element->findAll($selector, $locator);
$element->getText();
$element->getHtml();

$element->isVisible();
$element->click();
$element->press();
$element->check();
$element->uncheck();
Mink Cheat Sheet

THE END

- tests are awesome - tests are good for you, the client, and the product

- Behat Documentation - Behat in a box - No More Excuses - Follow me on twitter: @aczietlow