Entrez dans l'univers Behat ! Behat est un outil qui rend le "Développement Piloté par le Comportement" (Behavior Driven Development, ou BDD) possible. Avec le BDD, vous écrivez des spécifications, lisibles par des humains, qui décrivent le fonctionnement de votre application. Ces spécifications peuvent être exécutées pour en tester l'implémentation dans votre application. Et oui, c'est aussi cool que ça en a l'air !
par exemple, imaginez que vous souhaitez spécifier une application de listing
de fichier, à savoir la fameuse commande UNIX ls
.
Voilà comment ça se passerait :
Fonctionnalité: ls
Afin de visualiser l'arborescence d'un dossier
En tant qu'utilisateur UNIX
Je dois être capable de lister le contenu du répertoire courant
Scénario: Lister 2 fichiers dans un dossier
Etant donné que je suis dans le dossier "test"
Et que j'ai un fichier nommé "foo"
Et que j'ai un fichier nommé "bar"
Quand j'exécute la commande "ls"
Alors je dois obtenir :
"""
bar
foo
"""
Avec Behat, vous allez pouvoir exécuter ces scénarios et vérifier que la
commande ls
a le comportement prévu.
Behat peut être utilisé pour tester n'importe quoi, et même une application web grâce à la librairie Mink.
Note
Behat a été inspiré par le projet Ruby Cucumber.
behat est un exécutable que vous pouvez lancer en ligne de commande, afin d'exécuter une série de tests. Afin de commencer, assurez-vous que vous disposez au moins de la version 5.3.1 de PHP.
La méthode la plus simple pour installer Behat est de passer par Composer.
Créez un fichier composer.json
à la racine de votre projet
{
"require": {
"behat/behat": ">=2.2.2"
},
"config": {
"bin-dir": "bin/"
}
}
Puis téléchargez composer.phar
and et exécutez la commande install
:
$ wget -nc http://getcomposer.org/composer.phar
$ php composer.phar install
Vous pourrez désormais exécuter Behat :
$ bin/behat
Vous pouvez également installer Behat avec PEAR :
$ pear channel-discover pear.symfony.com
$ pear channel-discover pear.behat.org
$ pear install behat/behat
Vous pouvez exécuter Behat simplement en lançant la commande behat
:
$ behat
Une autre solution consiste à utiliser une archive PHAR :
$ wget https://github.com/downloads/Behat/Behat/behat.phar
Il suffit ensuite de lancer l'archive PHAR avec la commande php
:
$ php behat.phar
Enfin vous pouvez également cloner le projet avec Git en lançant :
$ git clone git://github.com/Behat/Behat.git && cd Behat
$ git submodule update --init
Puis téléchargez composer.phar
et lancez la commande install
:
$ wget -nc http://getcomposer.org/composer.phar
$ php composer.phar install
Vous pourrez ensuite exécuter Behat avec :
$ bin/behat
Dans cet exemple nous allons rapidement tester le comportement de la commande
UNIX ls
. Créez un nouveau dossier et initialisez y Behat :
$ mkdir ls_project
$ cd ls_project
$ behat --init
La commande behat --init
va créer un dossier features/
avec les
composants de base pour démarrer.
Tout dans Behat démarre avec une fonctionnalité. Par exemple, ici la
fonctionnalité consiste en la commande ls
du système UNIX, à savoir "lister
des fichiers". Commencez donc par créer le fichier features/ls.feature
:
# features/ls.feature
# language: fr
Fonctionnalité: ls
Afin de voir l'arboresence d'un dossier
En tant qu'utilisateur UNIX
Je dois être capable de lister le contenu du répertoire courant
Chaque fonctionnalité démarre de la même façon : une ligne qui nomme la fonctionnalité, suivie de trois lignes qui en décrivent le bénéfice, le rôle et la fonctionnalité elle-même.
Même si cette section est nécessaire, elle n'est pas indispensable pour Behat. Si elle est importante, c'est pour que votre fonctionnalité puisse être comprise et lisible par les autres lecteurs.
Remarquez la présence du commentaire # language: fr
. Ce commentaire va
indiquer à Behat que nous travaillons en Français.
Ensuite, ajoutez le scénario suivant à la fin du fichier
features/ls.feature
:
Scénario: Lister 2 fichiers dans un dossier
Etant donné que je suis dans le dossier "test"
Et que j'ai un fichier nommé "foo"
Et que j'ai un fichier nommé "bar"
Quand j'exécute la commande "ls"
Alors je dois obtenir :
"""
bar
foo
"""
Tip
la syntaxe spéciale """
dans les dernières lignes permet de définir des
étapes sur plusieurs lignes. ne vous préoccuppez pas pour le moment.
Chaque fonctionnalité est définie par un ou plusieurs "scénarios", qui décrivent la manière dont la fonctionnalité doit se comporter dans différentes conditions. C'est cette partie qui va se transformer en test. Chaque scénario suit toujours le même format de base :
Scénario: Une description du scénario
Etant donné [un contexte]
Quand [un événement]
Alors [un résultat attendu]
Chaque étape d'un scénario - le contexte, l'événement et le résultat
attendu - peut être étendue en ajoutant les mots clefs Et
et Mais
:
Scénario: Une description du scénario
Etant donné que [un contexte]
Et [plus d'informations sur le contexte]
Quand [un événement]
Et [un autre événement]
Alors [résultat attendu]
Et [un autre résultat attendu]
Mais [un autre résultat attendu]
Il n'y a pas de différence réelle entre Alors
, Et
ou Mais
, ou aucun
des mot-clefs qui démarrent chaque ligne. Ces mot-clefs sont sont simplement
disponibles dans vos scénarios pour en faciliter la lecture.
Vous venez de définir une fonctionnalité, ainsi que son premier scénario. Vous êtes prêt à voir Behat en action ! Exécutez Behat depuis le dossier de votre projet:
$ behat --lang=fr
Si tout fonctionne correctement, vous devriez voir quelque chose comme :
Note
Le paramètre lang=fr
permet de préciser à Behat de travailler en
Français. N'oubliez pas d'ajouter le commentaire # language: fr
au
début de vos fichiers de fonctionnalité.
Behat trouve automatiquement le fichier features/ls.feature
et tente
d'exécuter ses Scénarios
comme des tests. Cependant, nous n'avons pas
encore vu comment Behat fait pour comprendre des expressions comme Etant
donné que je suis dans le dossier "test"
, ce qui provoque une erreur.
En fait, Behat fait la correspondance entre chaque Etape
d'un Scénario
et une liste d'expressions régulières que vous pouvez définir. Autrement dit,
c'est votre boulot de dire à Behat ce que signifie Etant
donné que je suis dans le dossier "test"
. Heureusement, Behat vous aide et
affiche l'expression régulière dont vous avez probablement besoin pour définir
votre étape :
Vous pouvez implémenter les définitions d'étapes pour les étapes non définies avec ces modèles :
/**
* @Given /^que je suis dans le dossier "([^"]*)"$/
*/
public function queJeSuisDansLeDossier($argument1)
{
throw new PendingException();
}
Suivez les conseils de Behat et ajoutez ce qui suit au fichier
features/bootstrap/FeatureContext.php
.Renommez juste $argument1
en
$dir
, pour plus de clarté:
# features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
class FeatureContext extends BehatContext
{
/**
* @Given /^que je suis dans le dossier "([^"]*)"$/
*/
public function queJeSuisDansLeDossier($argument1)
{
if (!file_exists($dir)) {
mkdir($dir);
}
chdir($dir);
}
}
Très simplement, on a démarré par une expression régulière suggérée par Behat,
expression qui rend les valeurs entre guillemets (c'est-à-dire
"test") disponibles sous forme de variables (ici $dir
). Il suffit
maintenant, à l'intérieur de la méthode, de créer le dossier approprié et de
nous y déplacer.
Répétez l'opération pour les autres étapes non définies. Le fichier
FeatureContext.php
devrait ressembler à ceci :
# features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Exception\PendingException;
use Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
/**
* Features context.
*/
class FeatureContext extends BehatContext
{
/**
* @Given /^que je suis dans le dossier "([^"]*)"$/
*/
public function queJeSuisDansLeDossier($dir)
{
if (!file_exists($dir)) {
mkdir($dir);
}
chdir($dir);
}
/**
* @Given /^que j\'ai un fichier nommé "([^"]*)"$/
*/
public function queJAiUnFichierNomme($file)
{
touch($file);
}
/**
* @Given /^j\'exécute la commande "([^"]*)"$/
*/
public function jExecuteLaCommande($command)
{
exec($command, $output);
$this->output = trim(implode("\n", $output));
}
/**
* @Then /^je dois obtenir :$/
*/
public function jeDoisObtenir(PyStringNode $string)
{
if ((string) $string !== $this->output) {
throw new Exception(
"Actual output is:\n" . $this->output
);
}
}
}
Note
Quand vous utilisez des arguments multi-lignes - comme lorsque nous
avons utilisé la syntaxe """
plus haut - la valeur passée à la
méthode (c'est-à-dire $string
) est un objet qui peut être converti en
chaîne de caractères en utilisant la syntaxe (string) $string
, ou bien
$string->getRaw()
.
Bien ! Maintenant que vous avez défini toutes vos étapes, lancez à nouveau Behat: :
$ behat
Tout est valide ! Behat a exécuté chacune de vos étapes - créer un nouveau
dossier qui contient deux fichiers, puis exécuter la commande ls
- et a
comparé le résultat obtenu au résultat attendu.
Bien sûr, maintenant que vous avez défini vos étapes de base, ajouter des
scénarios à facile. Par exemple, ajoutez ce qui suit au fichier
features/ls.feature
. Vous aurez alors deux scénarios :
Scénario: Lister 2 fichiers d'un dossuer avec le paramètre -a
Etant donné que je suis dans le dossier "test"
Et que j'ai un fichier nommé "foo"
Et que j'ai un fichier nommé "bar"
Quand j'exécute la commande "ls -a"
Alors je dois obtenir :
"""
.
..
bar
foo
"""
Lancez à nouveau Behat. Cette fois, deux tests sont exécutés ; et les deux passent bien !
C'est tout ! Une fois que vous avez quelques étapes définies, vous pouvez
imaginez une foule de scénarios à rédiger pour la commande ls
. Bien sûr,
la même chose peut être réalisée pour tester des applications Web, et Behat
intègre une librairie très riche, appelée Mink, pour cela.
Bien sûr, il reste encore pas mal de choses à apprendre encore, y compris
en découvrir plus sur la :doc:`Syntaxe de Gherkin </guides/1.gherkin>` (le
langage utilisé dans le fichier ls.feature
).
La commande behat --init
initialise le dossier afin qu'il ressemble à ceci :
|-- features
`-- bootstrap
`-- FeatureContext.php
Tout ce qui à faire à Behat sera contenu dans le dossier features
, qui est
lui-même décomposé en trois zones :
features/
- Behat y recherche la liste des fichiers*.feature
à exécuterfeatures/bootstrap/
- Chaque fichier PHP (*.php
) présent sera automatiquement chargé par Behat avant que les tests ne soit lancésfeatures/bootstrap/FeatureContext.php
- Ce fichier contient la classe de Contexte dans laquelle chaque étape des scénarios sera exécutée
Comme vous l'avez déjà vu, une fonctionnalité est un simple et lisible fichier texte, dans un format appelé Gherkin. Chaque fonctionnalité suit ces quelques règles de base :
- Par convention, chaque fichier
*.feature
représente une seule fonctionnalité (comme la fonctionnalitéls
, la fonctionnalité enregistrement d'un utilisateur, etc.) - La fonctionnalité démarre par une ligne démarre avec le mot-clef
Fonctionnalité:
, suivi par son titre, puis trois lignes qui la décrivent. - Une fonctionnalité contient d'ordinaire une liste de scénarios. Vous pouvez écrire ce que vous voulez au dessus du premier scénario : ce texte sera alors consideré comme une simple description de la fonctionnalité.
- Chaque scénario démarre par le mot-clef
Scénario
, suivi par une courte description de dernier. Chaque scénario contient une liste d'étapes, qui doivent démarrer par l'un de ces mot-clefs :Etant donné que
,Quand
,Alors
,Et
ouMais
. Behat ne fait aucune distinction entre ces mot-clefs, mais vous pouvez les utiliser pour donner plus de sens à vos scénarios.
Behat va faire le lien entre, d'un côté le texte qui décrit une étape, de l'autre une expression régulière qui lui fait correspondre une définition.
Une définition d'étape est rédigée en PHP. Elle consiste en un mot-clef, une expression régulière et une méthode. Par exemple :
/**
* @Given /^que je suis dans le dossier "([^"]*)"$/
*/
public function queJeSuisDansLeDossier($dir)
{
// (...)
}
Quelques repères :
@Given
est un mot-clef de définition. Troismot-clefs sont autorisés dans les annotations :
@Given
/@When
/
@Then
. Ces trois mot-clefs de définition sont techniquement équivalent, ils ne servent qu'à vous permettre de donner plus de sens à vos étapes lorsqu'elles sont lues par des humains.Le texte qui suit le mot-clef de définition est une expression régulière (/^que je suis dans le dossier "([^"]*)"$/).
Chaque motif de recherche de l'expression régulière (
([^"]*)
) sera passé à la méthode sous forme de paramètre ($dir
).Si vous souhaitez, à l'intérieur d'une étape, signifier à Behat que quelque chose s'est mal passé, vous devez lancez une exception :
/** * @Given /^que je suis dans le dossier "([^"]*)"$/ */ public function queJeSuisDansLeDossier($dir) { // (...) if (...) { throw new Exception("explicit message"); } }
Tip
Behat ne dispose pas de son propre outil d'assertion, mais vous permet d'utiliser n'importe quel outil tiers. Par exemple, si vous êtes familié avec PHPUnit, vous pouvez utiliser ses assertions dans Behat :
# features/bootstrap/FeatureContext.php
<?php
use Behat\Behat\Context\BehatContext;
use Behat\Gherkin\Node\PyStringNode;
require_once 'PHPUnit/Autoload.php';
require_once 'PHPUnit/Framework/Assert/Functions.php';
class FeatureContext extends BehatContext
{
/**
* @Then /^je dois obtenir :$/
*/
public function jeDoisObtenir(PyStringNode $string)
{
$expected = (...);
assertEquals($string->getRaw(), $expected);
}
}
A contrario, toutes les étapes qui ne déclenchent pas d'exception seront considérées par Behat comme valides ("passées avec succès").
Vous trouverez plus d'informations à ce sujet dans le :doc:`Guide de définitions d'étape </guides/2.definitions>`
Behat crée un objet de contexte pour chaque scénario, puis exécute toutes les étapes de ce scénario dans ce même objet. En d'autres termes, si vous souhaitez partager des variables entre des étapes, vous pouvez le faire facilement en assignant des valeurs aux attributs de l'objet de contexte lui-même.
Vous pourrez en découvrir plus sur les Contextes
en consultant
":doc:`/guides/4.context`".
Behat contient un outil en lignes de commandes qui permet d'exécuter les tests. Cet outil dispose de différentes options.
Pour voir la liste des ces options (et leur description), exécutez :
$ behat -h
Il est important de souligner que cet outil vous permet d'obtenir la liste des toutes les définitions d'étapes que vous avez vous-même introduit dans le système. C'est un moyen simple de vous remémorer exactement ce que vous avez déjà défini auparavant :
$ behat -dl --lang=fr
Vous pourrez en découvrir plus sur Behat en ligns de commande dans ":doc:`/guides/6.cli`".
Félicitations ! Vous en savez désormais suffisamment pour démarrer avec le Développement Piloté par le Comportement et sur Behat.
Maintenant, vous pouvez en apprendre plus sur la :doc:`Syntaxe de Gherkin </guides/1.gherkin>`, ou bien découvrir comment tester une application web avec Behat et Mink.