Drupal camp paris 2013: présentation de Behat, Mink et Drupal extension
1. Introduction aux tests de recette automatisés
Avec l'extension Drupal pour Behat
didier.boff@ows.fr
B2F @ drupal.org
https://github.com/B2F
http:///twitter.com/zenelse
2. Les joies du test
Quand mon collègue avance sans plan de test
http://lesjoiesdutest.tumblr.com
26. Méthodologie
● Behavior Driven Development (BDD)
DrupalCon Portland 2013: BEHAT, BEHAVIORAL-
DRIVEN DEVELOPMENT AND SELENIUM IN DRUPAL
Ryan Weaver
https://portland2013.drupal.org
33. Méthodologie
● Gherkin
● Feature
○ Scenario
■ Given
● And...
■ When
● And...
Évènements
And I enter "Vietnam" for "pays"
And I enter "Bo Bun" for "plats"
And I enter "Paris 13" for "lieu"
34. Méthodologie
● Gherkin
● Feature
○ Scenario
■ Given
● And...
■ When
● And...
■ Then
● And...
Résultats
Then I should see the text "Pho Bida"
53. Développement
● Mink extension
http://mink.behat.org/
Given /^(?:|I )am on (?:|the )homepage$/
When /^(?:|I )go to (?:|the )homepage$/
Given /^(?:|I )am on "(?P<page>[^"]+)"$/
When /^(?:|I )go to "(?P<page>[^"]+)"$/
When /^(?:|I )reload the page$/
When /^(?:|I )move backward one page$/
When /^(?:|I )move forward one page$/
When /^(?:|I )press "(?P<button>(?:[^"]|")*)"$/
When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/
When /^(?:|I )fill in "(?P<field>(?:[^"]|")*)" with "(?P<value>(?:[^"]|")*)"$/
When /^(?:|I )fill in "(?P<field>(?:[^"]|")*)" with:$/
When /^(?:|I )fill in "(?P<value>(?:[^"]|")*)" for "(?P<field>(?:[^"]|")*)"$/
When /^(?:|I )fill in the following:$/
When /^(?:|I )select "(?P<option>(?:[^"]|")*)" from "(?P<select>(?:[^"]|")*)"$/
When /^(?:|I )additionally select "(?P<option>(?:[^"]|")*)" from "(?P<select>(?:[^"]|")*)"$/
...
bin/behat -dl
54. Développement
● Mink extension
http://mink.behat.org/
Presses button with specified id|name|title|alt|value.
@When /^(?:|I )press "(?P<button>(?:[^"]|")*)"$/
Clicks link with specified id|title|alt|text.
@When /^(?:|I )follow "(?P<link>(?:[^"]|")*)"$/
Behat/MinkExtension/Context/MinkContext.php
60. Développement
Installation: suites de tests
features/{name}.feature
Feature: un exemple
Quelques steps pour illustrer une feature
Scenario: vérification qu'un lien est présent
Given I am at "/home"
When I click "Se connecter"
Then I should see "login"
65. Développement
Installation: subcontexts
# features/bootstrap/FeatureContext.php
use BehatMinkExtensionContextRawMinkContext;
use BehatMinkExtensionContextMinkContext;
class FeatureContext extends RawMinkContext
{
public function __construct(array $parameters)
{
$this->useContext('mink', new MinkContext);
}
}
http://extensions.behat.org/mink/
66. Développement
Installation: subcontexts
# features/bootstrap/FeatureContext.php
use BehatMinkExtensionContextRawMinkContext;
use BehatMinkExtensionContextMinkContext;
class FeatureContext extends RawMinkContext
{
public function __construct(array $parameters)
{
$this->useContext('mink', new MinkContext);
}
}
http://extensions.behat.org/mink/
68. Développement
● Mink steps
When I hover the element ?
/**
* @When /^I hover "([^"]*)"$/
*/
public function iHover($cssId)
{
http://mink.behat.org/
When will this be over ?
69. Développement
● Mink steps
/**
* @When /^I hover "([^"]*)"$/
*/
public function iHover($cssId)
{
$page = $this->getSession()->getPage();
$element = $page->find('css', $cssId);
http://mink.behat.org/api/behat/mink/element/nodeelement.html
70. Développement
● Mink steps
public function iHover($cssId)
{
$page = $this->getSession()->getPage();
$element = $page->find('css', $cssId);
if (null === $element) {
throw new ElementNotFoundException($this->getSession(), 'element', 'css', $
}
http://mink.behat.org/api/behat/mink/element/nodeelement.html
71. Développement
● Mink steps
__construct(Session session, string type, string selector, string locator)
And I hover "a[title='abcdefghi']" # FeatureContext::iHover()
Element matching css "a[title='abcdefghi']" not found.
throw new ElementNotFoundException
($this->getSession(), 'element', 'css', $cssId);
http://mink.behat.org/api/behat/mink/element/nodeelement.html
72. Développement
● Mink steps
public function iHover($cssId)
{
$page = $this->getSession()->getPage();
$element = $page->find('css', $cssId);
if (null === $element) {
throw new ElementNotFoundException($this->getSession(), 'element', 'css', $cssId
}
$element->mouseOver();
http://mink.behat.org/api/behat/mink/element/nodeelement.html
73. Développement
● Mink steps
public function iHover($cssId)
{
$page = $this->getSession()->getPage();
$element = $page->find('css', $cssId);
if (null === $element) {
throw new ElementNotFoundException($this->getSession(), 'element', 'css', $cssId
}
$element->mouseOver();
Attention: Selenium supporte le
mouseOver Javascript only :(
http://mink.behat.org/api/behat/mink/element/nodeelement.html
74. Développement
● Mink steps - AJAX
/**
* Waits some time or until JS condition turns true.
*
* @param integer $time time in milliseconds
* @param string $condition JS condition
*/
public function wait($time, $condition = 'false')
{
$this->driver->wait($time, $condition);
}
http://mink.behat.org/api/source/behat/mink/session.php.html
75. Développement
● Mink steps - AJAX
/**
* @When /^I wait (?P<timing>d+)sec$/
*/
public function iWaitNSec($timing) {
$this->getSession()->wait($timing*1000);
}
http://mink.behat.org/api/source/behat/mink/session.php.html
76. Développement
● Mink steps - AJAX
/**
* @When /^I wait (?P<timing>d+)sec$/
*/
public function iWaitNSec($timing) {
$this->getSession()->wait($timing*1000);
}
http://mink.behat.org/api/source/behat/mink/session.php.html
94. Développement
● Drupal Extension: user steps
Given /^I am an anonymous user$/
Given /^I am not logged in$/
Given /^I am logged in as a user with the "(?P<role>[^"]*)" role$/
https://drupal.org/project/drupalextension
96. Opened blackbox
● Drupal Extension: user steps
# @see Drupal/DrupalExtension/Context/DrupalContext.php
/**
* Helper function to login the current user.
*/
public function login() {
...
$this->getSession()->visit($this->locatePath('/user'));
$element = $this->getSession()->getPage();
$element->fillField($this->getDrupalText('username_field'), $this->user->name);
$element->fillField($this->getDrupalText('password_field'), $this->user->pass);
$submit = $element->findButton($this->getDrupalText('log_in'));
https://drupal.org/project/drupalextension
97. Développement
● Drupal Extension: user steps
#behat.yml
DrupalDrupalExtensionExtension:
text:
log_out: "Sign out"
log_in: "Sign in"
password_field: "Enter your password"
username_field: "Nickname"
https://drupal.org/project/drupalextension
98. Développement
● Drupal Extension: content steps
Given /^I am viewing (?:a|an) "(?P<type>[^"]*)" node with the title "(?P<title>[^"
Given /^I am viewing (?:a|an) "(?P<vocabulary>[^"]*)" term with the name "(?
P<name>[^"]*)"$/
https://drupal.org/project/drupalextension
99. Développement
● Drupal Extension: region steps
Then /^I should see the "(?P<heading>[^"]*)" heading in the "(?P<region>[^"]*)"(?:|reg
When /^I (?:follow|click) "(?P<link>[^"]*)" in the "(?P<region>[^"]*)"(?:| region)$/
Then /^I should see the link "(?P<link>[^"]*)" in the "(?P<region>[^"]*)"(?:| region)$/
Given /^I press "(?P<button>[^"]*)" in the "(?P<region>[^"]*)"(?:| region)$/
https://drupal.org/project/drupalextension
100. Développement
● Drupal Extension: region steps
#behat.yml
DrupalDrupalExtensionExtension:
region_map:
My region: "#css-selector"
Content: "#main .region-content"
Right sidebar: "#sidebar-second"
https://drupal.org/project/drupalextension
101. Développement
● Drupal Extension: misc steps
Given /^the cache has been cleared$/
Given /^I run cron$/
https://drupal.org/project/drupalextension
104. Introduction aux tests de recette automatisés
Avec l'extension Drupal pour Behat
didier.boff@ows.fr
B2F @ drupal.org
https://github.com/B2F
http:///twitter.com/zenelse
Merci de votre attention