Drupal camp paris 2013: présentation de Behat, Mink et Drupal extension
-
Upload
didier-b2f -
Category
Technology
-
view
2.740 -
download
3
description
Transcript of Drupal camp paris 2013: présentation de Behat, Mink et Drupal extension
Introduction aux tests de recette automatisésAvec l'extension Drupal pour Behat
B2F @ drupal.orghttps://github.com/B2Fhttp:///twitter.com/zenelse
Les joies du testQuand mon collègue avance sans plan de test
http://lesjoiesdutest.tumblr.com
Plan
● Retour d'expérience○ Campus France○ Divers outils démodés
Plan
● Retour d'expérience○ Campus France○ Divers outils démodés
● Méthodologie○ BDD, Gherkin
Plan
● Retour d'expérience○ Campus France○ Divers outils démodés
● Méthodologie○ BDD, Gherkin
● Développement○ Behat/Mink○ MinkExtension (démo)○ DrupalExtension (démo)
Retour d'expérience
www.campusfrance.org
● Migration Drupal 6 -> 7
Retour d'expérience
www.campusfrance.org
● Migration Drupal 6 -> 7● 100 sites, domain access● multilingues
Retour d'expérience
www.campusfrance.org
● Migration Drupal 6 -> 7● 100 sites, domain access● multilingues
Quels tests ?
Retour d'expérience
SeleniumAPI
● click● type● select● addSelection● submit● ... http://release.seleniumhq.org/selenium-
core/
Retour d'expérience
SeleniumIDE
Retour d'expérience
?Test automatisé
SeleniumIDE
Retour d'expérience
SeleniumIDE
?Test automatisé
Multidomaine
Retour d'expérience
SeleniumIDE
?AJAXMultidomaine
Test automatisé
Retour d'expérience
Selenium Server
http://docs.seleniumhq.org/download/
java -jar selenium-server-standalone-<version-number>.jar
http://selenium.googlecode.com/files/selenium-server-standalone-2.33.0.jar
Retour d'expérience
Selenium Server+ PHPUnit
PHPUnit_Extensions_SeleniumTestCase
Retour d'expérience
Selenium Server+ PHPUnit
https://github.com/B2F/ows-phpunitclass OWSeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase {
public function __construct($name = NULL, array $data = array(), $dataName = '') {
Retour d'expérience
Selenium Server + PHPUnit + IDE
Retour d'expérience
Selenium Server + PHPUnit + IDE +Selenium IDE: PHP Formattershttps://github.com/brokenthumbs/PHP-Formatter
Retour d'expérience
Selenium Server + PHPUnit + IDE +Selenium IDE: PHP Formattershttps://github.com/brokenthumbs/PHP-Formatter
https://github.com/B2F/PHP-Formatter
Les joies du testQuand je fais un test un peu trop compliqué pour un truc tout simple
http://lesjoiesdutest.tumblr.com
Les joies du test
Quand je fais un test un peu trop compliqué pour un truc tout simple ?
Les joies du test
Quand je fais un test un peu trop compliqué pour un truc tout simple
Les joies du test
Quand je fais un test un peu trop compliqué pour un truc tout simple
19 content types
Les joies du test
Quand je fais un test un peu trop compliqué pour un truc tout simple
...
Méthodologie
● Behavior Driven Development (BDD)
Méthodologie
● Behavior Driven Development (BDD)DrupalCon Portland 2013: BEHAT, BEHAVIORAL-DRIVEN DEVELOPMENT AND SELENIUM IN DRUPALRyan Weaver
https://portland2013.drupal.org
Méthodologie
● Gherkin
Méthodologie
● Gherkin
● Feature Suite de tests
Méthodologie
● Gherkin
● Feature○ Scenario Test
Méthodologie
● Gherkin
● Feature○ Scenario
■ Given● And...
Contexte
Méthodologie
● Gherkin
● Feature○ Scenario
■ Given● And...
Contexte
Given I am on "/restaurants"
Méthodologie
● Gherkin
● Feature○ Scenario
■ Given● And...
■ When● And... Évènements
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"
Méthodologie
● Gherkin
● Feature○ Scenario
■ Given● And...
■ When● And...
■ Then● And...
Résultats
Then I should see the text "Pho Bida"
Opened Blackbox
● Cucumber
Gherkin + Ruby
Opened Blackbox
● Cucumber
=
Gherkin + Ruby
Opened Blackbox
● Cucumber
Gherkin + Ruby ?
Opened Blackbox
● Cucumber
Gherkin + PHP
Opened Blackbox
● Behat
http://behat.org/
=
Gherkin + PHP
Opened Blackbox
http://behat.org/
<?php
use Behat\Behat\Context\BehatContext
class FeatureContext extends BehatContext{// /**// * @Given /^I have done something with "([^"]*)"$/// */// public function iHaveDoneSomethingWith($argument)// {// doSomethingWith($argument);// }}
● Behat: ça fait quoi ?
Opened Blackbox
http://behat.org/
<?php
use Behat\Behat\Context\BehatContext
class FeatureContext extends BehatContext{// /**// * @Given /^I have done something with "([^"]*)"$/// */// public function iHaveDoneSomethingWith($argument)// {// doSomethingWith($argument);// }}
● Behat: ça fait quoi ?
Opened Blackbox
● Mink
Web acceptance testing
http://mink.behat.org/
Opened Blackbox
● Mink: drivers
$driver = new \Behat\Mink\Driver\ZombieDriver();
http://mink.behat.org/
Opened Blackbox
● Mink: drivers
$driver = new \Behat\Mink\Driver\SahiDriver('firefox');
http://mink.behat.org/
Opened Blackbox
● Mink: drivers
$driver = new \Behat\Mink\Driver\GoutteDriver();
http://mink.behat.org/
Opened Blackbox
● Mink: drivers
$client = new \Selenium\Client($host, $port);$driver = new \Behat\Mink\Driver\SeleniumDriver( 'firefox', 'base_url', $client);
http://mink.behat.org/
Opened Blackbox
● Mink: session
// init session:$session = new \Behat\Mink\Session($driver);
// start session:$session->start();
http://mink.behat.org/
$session->visit('http://paris2013.drupalcamp.fr');
Opened Blackbox
● Mink: méthodes
$page = $session->getPage();$element = $page->find('css', 'a#selector');$element->getText();
http://mink.behat.org/
Opened Blackbox
● Mink: méthodes
$page = $session->getPage();$element = $page->find('css', 'a#selector');$element->getText();
http://mink.behat.org/
Développement
● En pratique
http://mink.behat.org/
Behat + Mink ?
Développement
● Mink extension
http://mink.behat.org/
=Behat + Mink
Développement
● Mink extension
http://mink.behat.org/
STEPS
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
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
Développement
Installation
http://docs.behat.org/#cookbook
Developing Web Applications with Behat and Mink
Développement
Installationcomposer.json{ "require": { "behat/behat": "2.4.*@stable", "behat/mink": "1.4.*@stable", "behat/mink-extension": "*", "behat/mink-goutte-driver": "*", "behat/mink-selenium2-driver": "*" }, "minimum-stability": "dev", "config": { "bin-dir": "bin/" }} http://docs.behat.org/cookbook/behat_and_mink.html
Développement
Installation
$ curl http://getcomposer.org/installer | php$ php composer.phar install
http://docs.behat.org/cookbook/behat_and_mink.html
Développement
Installation
$ lsbin composer.json composer.lock composer.phar vendor
http://docs.behat.org/cookbook/behat_and_mink.html
Développement
Installation
$ bin/behat --init
-> features/
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"
Développement
Installation: customisation
$ bin/behat --init
-> features/bootstrap/FeatureContext.php
Développement
Installation: customisation
# features/bootstrap/FeatureContext.php
/** * Features context. */class FeatureContext extends BehatContext{...
Développement
Installation: customisation
# features/bootstrap/FeatureContext.php
/** * Features context. */class FeatureContext extends BehatContext{...
Développement
Installation: customisation
# features/bootstrap/FeatureContext.php
/** * Features context. */class FeatureContext extends MinkContext{...
Développement
Installation: subcontexts
# features/bootstrap/FeatureContext.php
use Behat\MinkExtension\Context\RawMinkContext;use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends RawMinkContext{ public function __construct(array $parameters) { $this->useContext('mink', new MinkContext); }}
http://extensions.behat.org/mink/
Développement
Installation: subcontexts
# features/bootstrap/FeatureContext.php
use Behat\MinkExtension\Context\RawMinkContext;use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends RawMinkContext{ public function __construct(array $parameters) { $this->useContext('mink', new MinkContext); }}
http://extensions.behat.org/mink/
Développement
● Mink steps
When will this be over ?
http://mink.behat.org/
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 ?
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
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); }
http://mink.behat.org/api/behat/mink/element/nodeelement.html
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
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
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
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
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
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
Développement
Démo
bin/behat features/demo.feature
Développement
Démo: colored output
bin/behat --ansi features/demo.feature
behat arguments: http://docs.behat.org/guides/6.cli.html
Développement
● OwsContext
https://github.com/B2F/OwsContext
public function iHover($cssId)public function iWaitNsec($timing)public function iClickTheElementMatching($selector)
Développement
● OwsContext
https://github.com/B2F/OwsContext+ (experimental)
public function iWaitNSecForTheText($timing, $text)public function iSwitchToTheIframeNamed($iframe)public function iSwitchBackFromIframe()
Développement
Configuration
# behat.ymldefault: extensions: Behat\MinkExtension\Extension: base_url: http://google.fr goutte: ~ selenium2: ~
Développement
Configuration● AJAX
@javascript Scenario: vérification qu'un lien est présent Given I am at "/home" When I follow "my account" Then I should see "my page title"
Développement
Configuration● Profiles = overrides
# behat.ymldefault:...custom: extensions: Behat\MinkExtension\Extension: base_url: http://another-url.com
Développement
Configuration● Profiles
# bin/behat --profile=custom features/tests.feature
Développement
Configuration● Filtres
# behat.ymlcustom:... extensions: ... filters: tags: "@custom-filter"
Développement
Configuration● Filtres
@javascript @custom-filter Scenario: vérification qu'un lien est présent Given I am at "/home" When I hover "Se connecter" Then I wait 1sec for the text "login"
Développement
● Drupal Extension
https://drupal.org/project/drupalextension
Développement
● Drupal Extension/** * Features context. */class DrupalContext extends MinkContext implements DrupalAwareInterface {
private $drupal, $drupalParameters;
/** * Basic auth user and password. */ public $basic_auth;...
https://drupal.org/project/drupalextension
Développement
● Drupal Extension: installation # composer.json{ "require": {
"drupal/drupal-extension": "*" }, "minimum-stability": "dev", "config": {
"bin-dir": "bin/" }}
https://drupal.org/project/drupalextension
Développement
● Drupal Extension: installtion
$ curl http://getcomposer.org/installer | php$ php composer.phar install
https://drupal.org/project/drupalextension
Développement
● Drupal Extension: configuration#behat.ymldefault: paths:
features: 'features' extensions:
Behat\MinkExtension\Extension: goutte: ~ selenium2: ~ base_url: http://www.campusfrance.org/
https://drupal.org/project/drupalextension
Développement
● Drupal Extension: configuration#behat.yml
default: paths:
features: 'features' extensions:
Behat\MinkExtension\Extension: goutte: ~ selenium2: ~ base_url: http://www.campusfrance.org/
Drupal\DrupalExtension\Extension: blackbox: ~
https://drupal.org/project/drupalextension
Développement
● Drupal Extension en pratique
bin/behat -dl => liste les steps
https://drupal.org/project/drupalextension
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
Développement
● Drupal Extension: configuration#behat.yml
default: paths:
features: 'features' extensions:
...Drupal\DrupalExtension\Extension:blackbox: ~
drush: alias: myDrushAlias
https://drupal.org/project/drupalextension
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
Développement
● Drupal Extension: user steps
#behat.yml
Drupal\DrupalExtension\Extension: text: log_out: "Sign out" log_in: "Sign in" password_field: "Enter your password" username_field: "Nickname"
https://drupal.org/project/drupalextension
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
Développement
● Drupal Extension: region steps
Then /^I should see the "(?P<heading>[^"]*)" heading in the "(?P<region>[^"]*)"(?:|region)$/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
Développement
● Drupal Extension: region steps
#behat.yml
Drupal\DrupalExtension\Extension: region_map:
My region: "#css-selector"Content: "#main .region-content"Right sidebar: "#sidebar-second"
https://drupal.org/project/drupalextension
Développement
● Drupal Extension: misc steps
Given /^the cache has been cleared$/Given /^I run cron$/
https://drupal.org/project/drupalextension
Développement
Démo
Développement
Conclusion:
Utilisez BDD pour faire des tests de régressions.
Introduction aux tests de recette automatisésAvec l'extension Drupal pour Behat
B2F @ drupal.orghttps://github.com/B2Fhttp:///twitter.com/zenelse
Merci de votre attention