Des tests 'MODERNES' pour Drupal 
Christophe Villeneuve 
@hellosct1
Qui... est Christophe Villeneuve ? 
<< 
afup – lemug.fr – mysql – mariadb – drupal – demoscene – firefoxos – drupagora – phptour – forumphp – solutionlinux – demoinparis – ici et maintenant – eyrolles – editions eni – programmez – linux pratique – webriver – phptv – neuros - elephpant
Support présentation aujourd'hui 
Disponible sur http://www.eyrolles.fr 
Drupal avancé – un CMS pour développeurs par Christophe Villeneuve et Vanessa Kovalsky David aux Editions EYROLLES
Expérience sur... Les tests 
Citations 
✔ « Des fois cela plante » 
✔ « On a testé, … 
il y a 3 mois lors de la dernière livraison... 
Pas eu le temps... »
L'importance des tests 
Pourquoi c'est utile ? Et L'intérêt... 
✔ Le code peut casser 
✔ Changer le nom d'un fichier 
✔ Pas le temps d'en faire 
✔ C'est long manuellement 
✔ Coûte du temps au début 
✔ En fait gain après 
✔ Nécessite de la rigueur et 
de la constante
Les différents types de tests 
Tests Unitaires 
● Module / classe / méthode 
Tests fonctionnels 
● l'interface utilisateur testée par le client 
Tests intégrations 
● Tester la navigation, et l'interface utilisateur 
Tests Automatisés 
Tests de charges 
Tests d'ergonomie 
Tests de sécurité
Tests unitaires... En pratique
Tests unitaires 
Déroulement Quoi tester ? 
✔ Existe-t-il ? 
✔ Ecrire un test qui échoue 
✔ Ecrire un test qui est bon 
✔ Un test de sécurité 
✔ Un test de contrôle 
✔ Un module 
✔ Une classe 
✔ Une méthode 
✔ Tester 
✔ Ce qui est important 
✔ Le risque de casser 
✔ Les parties que vous 
touchez souvent
Tests unitaires : Les API 
✔ jUnit 
✔ SimpleTest 
✔ Zend_test 
✔ PHPUnit 
✔ Atoum 
Roadmap 
✔ ... D7 D8 D9 
SimpleTest 
PHPUnit
Cas pratique avec PHPUnit
Installation 
En ligne de commandes 
# sudo apt-get install phpunit 
# pear channel-discover pear.phpunit.de 
# pear channel-discover components.ez.no 
# pear channel-discover pear.symfony-project.com 
En mode assistée 
Avec IDE 
- Windows 
- Eclipse PDT 
- Linux 
- Netbeans 
- Mac
Tests unitaires : cas 1 
Du code... +ieurs commentaires 
<?php 
class calculs 
{ 
public function add($a, $b) 
{ 
return $a + $b; 
} 
} 
?> 
<?php 
class calculs 
{ 
/** 
* @assert (0, 0) == 0 
* @assert (0, 1) == 1 
* @assert (1, 0) == 1 
* @assert (1, 1) == 2 
* @assert (1, 2) == 3 
*/ 
public function add($a, $b) 
{ 
return $a + $b; 
} 
} 
?>
Tests unitaires : cas 1 
1 commentaire Passer au test 
<?php 
class calculs 
{ 
/** 
* @assert (1, 1) == 2 
*/ 
public function add($a, $b) 
{ 
return $a + $b; 
} 
} 
?> 
<?php 
/** 
* Generated from @assert (1, 1) == 2 
*/ 
public function testAdd() 
{ 
$this->assertEquals( 
2, 
$this->object->add(1, 1) 
); 
} 
?>
Fonctionnement PHPUnit avec Drupal 
✔ Boostrap 
✔ Script d'amorcage 
✔ Construction autonome 
✔ Etendre le Boostrap 
✔ Emplacement du fichier 
✔ Déclarer les SLASHs 
✔ Recherche de racine Drupal 
✔ Chargeons boostrap.inc 
✔ Execution
PHPUnit avec Drupal : Comprendre 
<?php 
define('DRUPAL_ROOT', realpath(dirname(__FILE__) ) . DS ); 
set_include_path(DRUPAL_ROOT . get_include_path()); 
include_once DRUPAL_ROOT . 'includes' . DS . 'bootstrap.inc'; 
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 
class base_test extends PHPUnit_Framework_TestCase 
{ 
public function test_one_more_one_is_two() 
{ 
$this->assertEquals(1+1,2); 
} 
} 
?>
Résultat 
. Le test est réussi 
F l'assertion a échoué 
E Erreur Exécution 
S Problème inconnu 
I Test incomplet
Module & PHPUnit & Drupal
Module & PHPUnit & Drupal (1/2) 
<?php 
/** 
* Implementation hook_help() 
*/ 
function drupalcampmontreal_help($path, $arg) { 
switch ($path) { 
case 'admin/help#drupalcampmontreal': 
$output = '<h3>A propos de : DrupalCamp Montréal</h3>'; 
$output .= '<p>Exemple de module test DrupalCamp Montréal 2014</p>'; 
return $output; 
} 
} 
✔ 
drupalmontreal.module
Module & PHPUnit & Drupal (2/2) 
drupalcampmontreal.test.php 
<?php 
include_once DRUPAL_ROOT . 'includes/bootstrap.inc'; 
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 
class drupalcampmontreal_test extends PHPUnit_Framework_TestCase 
{ 
public function test_function_help() 
{ 
$output = '<h3>A propos de : DrupalCamp Montréal</h3>'; 
$output .= '<p>Exemple de module test DrupalCamp Montréal 2014</p>'; 
$this->assertEquals(drupalcampmontreal_help('admin/help#drupalcampmontreal'), $output ); 
} 
...
Résultat
Tests Fonctionnels... En pratique
Tests Fonctionnels 
Déroulement Quoi tester ? 
✔ Prépare outil & API 
✔ Vérifie l'intégration des 
différents composants 
✔ On le partage 
✔ On récupère le résultat 
✔ Nous... Rien 
✔ C'est le client
Tests Fonctionnels : utilisation 
Outils 
✔ Selenium 
✔ Casper.js & Phantom.js 
✔ Cucumber 
✔ GreenPepper 
✔ ... 
D7 D8 D9 
Roadmap 
Selenium 
Casper.js 
Phantom.js
Cas pratique avec Selenium
Tests Fonctionnels : utilisation
Différents Selenium 
● Selenium *.jar 
● Selenium IDE 
– Extension Firefox 
● SeleniumHQ 
– Compatible avec tous les navigateurs 
http://www.seleniumhq.org/
Utilisation de Selenium
Selenium seul
Selenium et Drupal 
✔ Module selenium 
✔ S'appuie sur SimpleTest 
Selenium pour PHPUnit
Installation PHP Unit & Selenium 
# sudo apt-get install phpunit 
# sudo apt-get install php5-curl 
# sudo apt-get install php-selenium 
En mode assistée 
- Windows 
- Linux 
- Mac 
Avec IDE 
- Eclipse PDT 
- Netbeans
Utilisation PHP Unit & Selenium (1/2) 
<?php 
class TestLogin extends PHPUnit_Extensions_Selenium2TestCase { 
public function setUp() 
{ 
$this->setHost('localhost'); 
$this->setPort(4444); 
$this->setBrowser('firefox'); 
$this->setBrowserUrl('http://localhost/seleniumTut'); 
} 
public function testHasLoginForm() 
{ 
$this->url('index.php'); 
$username = $this->byName('username'); 
$password = $this->byName('password'); 
$this->assertEquals('', $username->value()); 
$this->assertEquals('', $password->value()); 
}
Utilisation PHP Unit & Selenium (2/2) 
<?php 
public function testSubmitButtonIsDisabledUntilFieldsAreFilled() 
{ 
$this->url('index.php'); 
$username = $this->byName('username'); 
$password = $this->byName('password'); 
$submit = $this->byId('submit'); 
$this->assertFalse($submit->enabled()); 
$username->value('votreLogin'); 
$password->value('votrePassword'); 
$this->assertTrue($submit->enabled()); 
$username->clear(); 
$password->clear(); 
$username->value(' '); 
$this->assertFalse($submit->enabled()); 
} 
?>
PHPUnit / Selenium / Drupal (1/3)
PHPUnit / Selenium / Drupal (2/3) 
<?php 
class drupalcampmontreal_test extends PHPUnit_Extensions_Selenium2TestCase { 
public function setUp() 
{ 
$this->setHost('localhost'); 
$this->setPort(4444); 
$this->setBrowser('firefox'); 
$this->setBrowserUrl('http://localhost/drupal7'); 
} 
public function testSite() 
{ 
$this->setBrowserurl ('index.php'); 
}
PHPUnit / Selenium / Drupal (3/3) 
public function testOuverturePageTypeMenu() { 
$this->open ( 
"http://localhost/drupal-test/admin/menus/diner" ); 
$this->assertElementValueEquals ( 'name', '' ); 
$this->assertElementValueEquals ( 'listlieu', '' ); 
}
Au final... les tests associés 
$ cd votreSite 
$ phpunit sites/all/modules/menus/menus.test.php
Tests Intégrations... En pratique
Tests Intégrations 
Déroulement Pourquoi ? 
✔ 1 Navigation de la prod. 
✔ Générer le script pour 
repérer les modifications 
de comportement 
✔ 1 exécution sur le 
nouveau serveur 
✔ Simulation d'un être 
humain 
✔ Détection des problèmes 
éventuels
Tests Intégrations : utilisation 
Outils 
✔ Selenium 
✔ Sikuli 
✔ Hudson / Jenkins 
✔ PhpUnderControl 
✔ Behat/Mink 
✔ Sahi 
✔ PHPCov 
✔ ... 
D7 D8 D9 
Roadmap 
Selenium 
Behat+mink
Cas pratique avec Behat
Utilisation 
# pear channel-discover pear.symfony.com 
# pear channel-discover pear.behat.org 
# pear install behat/behat 
http://docs.behat.org/ 
{ 
"require": { 
"behat/behat": 
"2.4.*@stable" 
}, 
"minimum-stability": "dev", 
"config": { 
"bin-dir": "bin/" 
} 
} 
Contexte 
Evénements 
Résultat
Utilisation par le code Behat/Mink 
require_once 'mink/autoload.php'; 
class FeatureContext extends BehatMinkBehatContextMinkContext { 
/** 
* @Given /^Je suis identifie "([^"]*)"$/ 
*/ 
public function jeSuisIdentifie($username) 
{ 
return array( 
new StepGiven('I go to "login.php"') 
,new StepWhen("I fill in 'Mon nom' with '$username' ") 
,new StepWhen("I fill in 'Mon mot de passe' with '$password' ") 
,new StepWhen('I press "Login"') 
); 
} 
}
Behat / Mint / Drupal (1/3) 
Module behat 
# curl -s https://getcomposer.org/installer | php 
# php composer.phar install 
# wget http://selenium...../selenium-server-standalone-2.26.0.jar 
Behat.yml 
default: 
context: 
extensions: 
BehatMinkExtensionExtension: 
base_url: 'http://localhost' 
goutte: ~ 
selenium2: ~
Behat / Mint / Drupal (2/3) 
menus.feature 
Scenario: Submits diner when required fields are filled out 
Given I am on "/" 
When I follow "diner" 
And I fill in "Your name" with "#name" 
And I fill in "Your lieu" with "#lieu" 
And I press "add" 
Then I should see "diner ajouter"
Behat / Mint / Drupal (3/3) 
$ bin/behat features/menus.feature 
© http://drupalwatchdog.com/volume-2/issue-2/behat-and-mink
Les autres Tests …. 
Ils seront 
abordés 
dans la 
journée 
Ils seront 
abordés 
dans la 
journée
Merci 
http://blog.hello-design.fr 
@hellosct1 Questions ?

Des tests modernes pour Drupal

  • 1.
    Des tests 'MODERNES'pour Drupal Christophe Villeneuve @hellosct1
  • 2.
    Qui... est ChristopheVilleneuve ? << afup – lemug.fr – mysql – mariadb – drupal – demoscene – firefoxos – drupagora – phptour – forumphp – solutionlinux – demoinparis – ici et maintenant – eyrolles – editions eni – programmez – linux pratique – webriver – phptv – neuros - elephpant
  • 3.
    Support présentation aujourd'hui Disponible sur http://www.eyrolles.fr Drupal avancé – un CMS pour développeurs par Christophe Villeneuve et Vanessa Kovalsky David aux Editions EYROLLES
  • 4.
    Expérience sur... Lestests Citations ✔ « Des fois cela plante » ✔ « On a testé, … il y a 3 mois lors de la dernière livraison... Pas eu le temps... »
  • 5.
    L'importance des tests Pourquoi c'est utile ? Et L'intérêt... ✔ Le code peut casser ✔ Changer le nom d'un fichier ✔ Pas le temps d'en faire ✔ C'est long manuellement ✔ Coûte du temps au début ✔ En fait gain après ✔ Nécessite de la rigueur et de la constante
  • 6.
    Les différents typesde tests Tests Unitaires ● Module / classe / méthode Tests fonctionnels ● l'interface utilisateur testée par le client Tests intégrations ● Tester la navigation, et l'interface utilisateur Tests Automatisés Tests de charges Tests d'ergonomie Tests de sécurité
  • 7.
  • 8.
    Tests unitaires DéroulementQuoi tester ? ✔ Existe-t-il ? ✔ Ecrire un test qui échoue ✔ Ecrire un test qui est bon ✔ Un test de sécurité ✔ Un test de contrôle ✔ Un module ✔ Une classe ✔ Une méthode ✔ Tester ✔ Ce qui est important ✔ Le risque de casser ✔ Les parties que vous touchez souvent
  • 9.
    Tests unitaires :Les API ✔ jUnit ✔ SimpleTest ✔ Zend_test ✔ PHPUnit ✔ Atoum Roadmap ✔ ... D7 D8 D9 SimpleTest PHPUnit
  • 10.
  • 11.
    Installation En lignede commandes # sudo apt-get install phpunit # pear channel-discover pear.phpunit.de # pear channel-discover components.ez.no # pear channel-discover pear.symfony-project.com En mode assistée Avec IDE - Windows - Eclipse PDT - Linux - Netbeans - Mac
  • 12.
    Tests unitaires :cas 1 Du code... +ieurs commentaires <?php class calculs { public function add($a, $b) { return $a + $b; } } ?> <?php class calculs { /** * @assert (0, 0) == 0 * @assert (0, 1) == 1 * @assert (1, 0) == 1 * @assert (1, 1) == 2 * @assert (1, 2) == 3 */ public function add($a, $b) { return $a + $b; } } ?>
  • 13.
    Tests unitaires :cas 1 1 commentaire Passer au test <?php class calculs { /** * @assert (1, 1) == 2 */ public function add($a, $b) { return $a + $b; } } ?> <?php /** * Generated from @assert (1, 1) == 2 */ public function testAdd() { $this->assertEquals( 2, $this->object->add(1, 1) ); } ?>
  • 14.
    Fonctionnement PHPUnit avecDrupal ✔ Boostrap ✔ Script d'amorcage ✔ Construction autonome ✔ Etendre le Boostrap ✔ Emplacement du fichier ✔ Déclarer les SLASHs ✔ Recherche de racine Drupal ✔ Chargeons boostrap.inc ✔ Execution
  • 15.
    PHPUnit avec Drupal: Comprendre <?php define('DRUPAL_ROOT', realpath(dirname(__FILE__) ) . DS ); set_include_path(DRUPAL_ROOT . get_include_path()); include_once DRUPAL_ROOT . 'includes' . DS . 'bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); class base_test extends PHPUnit_Framework_TestCase { public function test_one_more_one_is_two() { $this->assertEquals(1+1,2); } } ?>
  • 16.
    Résultat . Letest est réussi F l'assertion a échoué E Erreur Exécution S Problème inconnu I Test incomplet
  • 17.
  • 18.
    Module & PHPUnit& Drupal (1/2) <?php /** * Implementation hook_help() */ function drupalcampmontreal_help($path, $arg) { switch ($path) { case 'admin/help#drupalcampmontreal': $output = '<h3>A propos de : DrupalCamp Montréal</h3>'; $output .= '<p>Exemple de module test DrupalCamp Montréal 2014</p>'; return $output; } } ✔ drupalmontreal.module
  • 19.
    Module & PHPUnit& Drupal (2/2) drupalcampmontreal.test.php <?php include_once DRUPAL_ROOT . 'includes/bootstrap.inc'; drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); class drupalcampmontreal_test extends PHPUnit_Framework_TestCase { public function test_function_help() { $output = '<h3>A propos de : DrupalCamp Montréal</h3>'; $output .= '<p>Exemple de module test DrupalCamp Montréal 2014</p>'; $this->assertEquals(drupalcampmontreal_help('admin/help#drupalcampmontreal'), $output ); } ...
  • 20.
  • 21.
  • 22.
    Tests Fonctionnels DéroulementQuoi tester ? ✔ Prépare outil & API ✔ Vérifie l'intégration des différents composants ✔ On le partage ✔ On récupère le résultat ✔ Nous... Rien ✔ C'est le client
  • 23.
    Tests Fonctionnels :utilisation Outils ✔ Selenium ✔ Casper.js & Phantom.js ✔ Cucumber ✔ GreenPepper ✔ ... D7 D8 D9 Roadmap Selenium Casper.js Phantom.js
  • 24.
  • 25.
  • 26.
    Différents Selenium ●Selenium *.jar ● Selenium IDE – Extension Firefox ● SeleniumHQ – Compatible avec tous les navigateurs http://www.seleniumhq.org/
  • 27.
  • 28.
  • 29.
    Selenium et Drupal ✔ Module selenium ✔ S'appuie sur SimpleTest Selenium pour PHPUnit
  • 30.
    Installation PHP Unit& Selenium # sudo apt-get install phpunit # sudo apt-get install php5-curl # sudo apt-get install php-selenium En mode assistée - Windows - Linux - Mac Avec IDE - Eclipse PDT - Netbeans
  • 31.
    Utilisation PHP Unit& Selenium (1/2) <?php class TestLogin extends PHPUnit_Extensions_Selenium2TestCase { public function setUp() { $this->setHost('localhost'); $this->setPort(4444); $this->setBrowser('firefox'); $this->setBrowserUrl('http://localhost/seleniumTut'); } public function testHasLoginForm() { $this->url('index.php'); $username = $this->byName('username'); $password = $this->byName('password'); $this->assertEquals('', $username->value()); $this->assertEquals('', $password->value()); }
  • 32.
    Utilisation PHP Unit& Selenium (2/2) <?php public function testSubmitButtonIsDisabledUntilFieldsAreFilled() { $this->url('index.php'); $username = $this->byName('username'); $password = $this->byName('password'); $submit = $this->byId('submit'); $this->assertFalse($submit->enabled()); $username->value('votreLogin'); $password->value('votrePassword'); $this->assertTrue($submit->enabled()); $username->clear(); $password->clear(); $username->value(' '); $this->assertFalse($submit->enabled()); } ?>
  • 33.
    PHPUnit / Selenium/ Drupal (1/3)
  • 34.
    PHPUnit / Selenium/ Drupal (2/3) <?php class drupalcampmontreal_test extends PHPUnit_Extensions_Selenium2TestCase { public function setUp() { $this->setHost('localhost'); $this->setPort(4444); $this->setBrowser('firefox'); $this->setBrowserUrl('http://localhost/drupal7'); } public function testSite() { $this->setBrowserurl ('index.php'); }
  • 35.
    PHPUnit / Selenium/ Drupal (3/3) public function testOuverturePageTypeMenu() { $this->open ( "http://localhost/drupal-test/admin/menus/diner" ); $this->assertElementValueEquals ( 'name', '' ); $this->assertElementValueEquals ( 'listlieu', '' ); }
  • 36.
    Au final... lestests associés $ cd votreSite $ phpunit sites/all/modules/menus/menus.test.php
  • 37.
  • 38.
    Tests Intégrations DéroulementPourquoi ? ✔ 1 Navigation de la prod. ✔ Générer le script pour repérer les modifications de comportement ✔ 1 exécution sur le nouveau serveur ✔ Simulation d'un être humain ✔ Détection des problèmes éventuels
  • 39.
    Tests Intégrations :utilisation Outils ✔ Selenium ✔ Sikuli ✔ Hudson / Jenkins ✔ PhpUnderControl ✔ Behat/Mink ✔ Sahi ✔ PHPCov ✔ ... D7 D8 D9 Roadmap Selenium Behat+mink
  • 40.
  • 41.
    Utilisation # pearchannel-discover pear.symfony.com # pear channel-discover pear.behat.org # pear install behat/behat http://docs.behat.org/ { "require": { "behat/behat": "2.4.*@stable" }, "minimum-stability": "dev", "config": { "bin-dir": "bin/" } } Contexte Evénements Résultat
  • 42.
    Utilisation par lecode Behat/Mink require_once 'mink/autoload.php'; class FeatureContext extends BehatMinkBehatContextMinkContext { /** * @Given /^Je suis identifie "([^"]*)"$/ */ public function jeSuisIdentifie($username) { return array( new StepGiven('I go to "login.php"') ,new StepWhen("I fill in 'Mon nom' with '$username' ") ,new StepWhen("I fill in 'Mon mot de passe' with '$password' ") ,new StepWhen('I press "Login"') ); } }
  • 43.
    Behat / Mint/ Drupal (1/3) Module behat # curl -s https://getcomposer.org/installer | php # php composer.phar install # wget http://selenium...../selenium-server-standalone-2.26.0.jar Behat.yml default: context: extensions: BehatMinkExtensionExtension: base_url: 'http://localhost' goutte: ~ selenium2: ~
  • 44.
    Behat / Mint/ Drupal (2/3) menus.feature Scenario: Submits diner when required fields are filled out Given I am on "/" When I follow "diner" And I fill in "Your name" with "#name" And I fill in "Your lieu" with "#lieu" And I press "add" Then I should see "diner ajouter"
  • 45.
    Behat / Mint/ Drupal (3/3) $ bin/behat features/menus.feature © http://drupalwatchdog.com/volume-2/issue-2/behat-and-mink
  • 46.
    Les autres Tests…. Ils seront abordés dans la journée Ils seront abordés dans la journée
  • 47.