11/03/2012

Behat – jour 2 : Installation et premiers tests

Maintenant qu'on a vu à quoi sert Behat, il est temps de passer à la pratique...

Vous trouverez une archive contenant toutes les sources de ce billet en bas de cette page.

Installer Behat

Le plus simple à mon goût : passer par pear :

pear channel-discover pear.symfony.com
<p>pear channel-discover pear.behat.org</p>
<p>pear install behat/behat

Un petit test pour vérifier que tout s'est bien passé :

behat --version

Démarrer un projet

Voici la structure initiale de mon exemple :

mkdir -p  application/library tests/product
Arborescence du projet Behat

Placez-vous dans le dossier tests/product, puis tapez :

behat --init

Cela a pour effet de créer les dossiers nécessaires à votre projet :

  • features : les fichiers de fonctionnalité *.feature
  • features/bootstrap : les classes nécessaires au fonctionnement des fonctionnalités (contextes)
  • features/bootstrap/FeatureContext.php : le contexte principal

Première fonctionnalité

Il est temps de démarrer. Notre client (product owner) souhaite une application dans laquelle on puisse gérer un compte bancaire : on peut consulter son solde, ajouter des sous, en retirer... et on n'a pas le droit d'être découvert.

Le fichier de fonctionnalité livré par notre client ressemble à ceci >feature/banque.feature< :

# language: fr
<p>Fonctionnalité: posséder un compte bancaire</p>
  Afin de gérer les comptes bancaires des utilisateurs
  En tant que client
  Je dois être capable d'effectuer des opérations basique sur mon compte

  Scénario: Avoir un compte bancaire valide
    Etant donné que je suis un nouveau client
    Alors je dois avoir "0" euros sur mon compte

  Scénario: Retirer de l'argent sur mon compte
    Etant donné que je suis un client
    Et que je possède "50" euros sur mon compte
    Quand je retire "10" euros
    Alors je dois avoir "40" euros sur mon compte

  Plan du Scénario: Ajouter de l'argent sur mon compte
    Etant donné que je suis un client
    Et que je possède "<soldeInitial>" euros sur mon compte
    Quand je dépose "<montant>" euros
    Alors je dois avoir "<soldeFinal>" euros sur mon compte

    Exemples:
      | soldeInitial    | montant | soldeFinal |
      | 0               | 10      | 10         |
      | 15              | 5       | 20         |
      | 35              | 5       | 40         |

  Scénario: Interdire les découverts
    Etant donné que je suis un client
    Quand j'essaye de retirer plus d argent que je n en ai sur mon compte
    Alors j'ai un message d erreur "Vous ne pouvez pas être à découvert"

Le product owner sait ce qu'il veut, il nous a même donné des exemples pour mieux nous orienter dans notre développement.

Notez au passage le commentaire "# language: fr" au début du fichier. Il indique que notre fonctionnalité est décrite en français.

Lancer les tests

Il va être temps de lancer behat. Placez-vous dans le dossier tests/product/feature, puis tapez simplement :

behat --lang=fr
(N'oubliez pas de préciser la langue, vu qu'on travaille pour l'instant en français). On va dérouler ensemble le résultat de cette commande :

Rappel de la fonctionnalité

On commence par un rappel de la fonctionnalité et des scénarios testés.

Fonctionnalité Behat

S'il y avait eu des erreurs, le texte aurait été écrit en rouge, et si tout avait été ok, il serait vert. Là le texte est orange : il n'a pas encore de signification par rapport à notre produit.

Bilan du test

On a ensuite des informations sur les tests :

Behat - résultats : en attente

Donner du sens aux fonctionnalités

Toutes nos étapes sont en attente de définition. En effet, on n'a rien qui fait le lien entre les scénarios (les souhaits du product owner) et notre produit (le code source). C'est là que Behat va être à mon sens magique : il nous fournit le code PHP nécessaire pour créer ce lien, avec pour chaque phrase :

  • l'annotation qui permet de faire le lien (@Given /^que je suis un nouveau client$/)
  • la méthode a insérer pour donner du sens à cette phrase (public function queJeSuisUnNouveauClient())
  • les valeurs de cas du scénario (encadrés par des guillemets), qui sont fournis en paramètres de la méthode

Voici donc la dernière étape du traitement du résultat de la commande :

Behat : donner du sens aux fonctionnalités

Behat nous fourni le code a copier-coller vers la classe qui gère notre contexte principal, à savoir la classe FeatureContext contenue dans /features/bootstrap/FeatureContext.php. Allons -y : copiez le code dans le fichier .

Vous pouvez constater que chaque méthode lance une exception de type PendingException. Cela signifie qu'il va falloir modifier ces méthodes pour les relier à notre application.

Pour vous simplifier la vie, je vous propose de télécharger directement le code nécessaire au bon fonctionnement de ces tests : Account.php, à placer dans application/library. Sinon libre à vous d'écrire le code qui correspondra à l'application ; en soit, peu importe le code, ce qui nous intéresse c'est le produit ;-)

Nous allons maintenant convertir le texte (langue naturelle) en code (source) :

<!--?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; require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Framework/Assert/Functions.php'; require_once __DIR__ . '/../../../../application/library/Account.php'; use \MyApp\Account as Account; /**  * Features context.  */ class FeatureContext extends BehatContext {     /**      * Testes account      *      * @var \MyApp\Account      */     private $_account;     /**      * Contains the last exception      *      * @var \Exception      */     private $_lastException;     /**      * @Given /^que je suis un nouveau client$/      */     public function queJeSuisUnNouveauClient() {         $this--->_account = new Account;
    }

    /**
     * @Then /^je dois avoir "([^"]*)" euros sur mon compte$/
     */
    public function jeDoisAvoirEurosSurMonCompte($balance) {
        assertEquals($balance, $this->_account->getBalance());
    }

    /**
     * @Given /^que je suis un client$/
     */
    public function queJeSuisUnClient() {
        if(is_null($this->_account)) {
            $this->_account = new Account;
        }
    }

    /**
     * @Given /^que je possède "([^"]*)" euros sur mon compte$/
     */
    public function queJePossedeEurosSurMonCompte($balance) {
        $this->_account->setBalance($balance);
    }

    /**
     * @Given /^je retire "([^"]*)" euros$/
     */
    public function jeRetireEuros($amount) {
        $this->_account->takeMoney($amount);
    }

    /**
     * @Given /^je dépose "([^"]*)" euros$/
     */
    public function jeDeposeEuros($amount) {
        $this->_account->addMoney($amount);
    }

    /**
     * @Given /^j\'essaye de retirer plus d argent que je n en ai sur mon compte$/
     */
    public function jEssayeDeRetirerPlusDArgentQueJeNEnAiSurMonCompte() {
        try {
            $this->_account->setBalance(50);
        $this->_account->takeMoney(100);
        } catch (\Exception $e) {
            $this->_lastException = $e;
        }
    }

    /**
     * @Given /^j\'ai un message d erreur "([^"]*)"$/
     */
    public function jAiUnMessageDErreur($message) {
        assertEquals($message, $this->_lastException->getMessage());
    }

}

Vous constaterez que :

  • on utilise PHPUnit en mode "fonction" pour nos assertions
  • chaque méthode correspond à une phase d'un scénario
  • on fait ce qu'on veut à l'intérieur de notre contexte ^^
  • c'est simple : il ne faut même pas 3 minutes pour écrire ce code

L'heure de vérité

Le suspense est à son comble : notre code correspond t--il au souhait du client quant à son produit ?

behat --lang=fr
Behat : le produit est conforme

Conclusion

Si besoin voici une archive contenant l'ensemble de ce projet : Découverte de behat - jour 2.zip.

Notre client (product owner) peut désormais s'assurer à tout moment que son produit est valide.Bien plus, il va pouvoir ajuster son produit au fur et à mesure des sprints en vous décrivant clairement son besoin : fini les specs de 500 pages, c'est le product owner qui est maître de son produit ! Et cela grâce à Behat.

La prochaine fois on verra comment mieux organiser son code : sous-contextes, configuration... et surtout comment tester une application web, grâce à Mink. mais en attendant, toute remarque / commentaire est le bienvenu ;-)

Je vous rappelle qu'une cheat sheet pour behat est disponible.

blog comments powered by Disqus