19/03/2012

Behat – jour 3 : Tester une application web avec Mink

On a vu précédemment ce qu'était Behat et comment tester une application simple en php avec Behat.

Maintenant allons plus loin et voyons comment tester une application web (à la quelle on accède par un navigateur). Et oui c'est possible ! :-)

Behat et ... Mink

On l'a dit : Behat permet de tester un produit. Ceci dit, il est rare qu'un client vous demande une application en ligne de commande ; le plus souvent le produit en question va être constitué de pages web. Qu'à cela ne tienne, Behat est très fortement lié à un autre outil : Mink.

Mink c'est quoi ? Et bien Mink va vous permettre :

  • ou bien de simuler un navigateur pour interagir avec votre application (produit)
  • ou bien de piloter un vrai navigateur (Firefox, Chrome...) pour interagir avec le produit

Voyons comment le lancer en mode "simulation" dans un premier temps.

Application de test

Pour vous éviter de perdre du temps, je vous propose de télécharger une petite page web toute simple qui va correspondre à notre produit de test :

  • une seule page pour consulter notre compte et ajouter/retirer des sous
  • un formulaire de connexion (pas de mot de passe)
  • les informations sont stockées en session

Ce qui donne ça :

Pour télécharger l'application exemple, ça se passe ici : behat-jour3-appli-web.zip (15 Ko).

Notre produit web

N'oublions pas que le client (product owner) reste le maître de son produit. Il nous fournit donc un fichier de fonctionnalités pour décrire son produit.

Remarque : la fonctionnalité est décrite en anglais ; bien que très actif, utiliser behat et mink en français reste aujourd'hui très aventureux à mon goût, ne serait-ce qu'à cause des nombreux apostrophes présents dans notre langue qui posent des difficultés.

Fichier bank.feature :

<p>Feature: Manage a bank account</p>
  In order to manage my account
  As a logged in user
  I need to be able to add or take mon ey on my account

  Background:
    And I am logged in as "jeanfrancois"
    And I have "50" euro
    And I am on "/"

  Scenario: Check my bank account
    Then I should see "You have 50 euro on your account"

  Scenario Outline: Add money
    Given I have "<initialAmount>" euro
    When I select "<operation>" from "Operation"
    And I fill in "Amount" with "<amount>"
    And I press "Go"
    Then I should see "You have <finalAmount> euro on your account"

    Examples:
    | operation   | initialAmount | amount    | finalAmount   |
    | Add money   | 50            | 10        | 60            |
    | Add money   | 50            | 20        | 70            |
    | Add money   | 50            | 5         | 55            |
    | Add money   | 50            | 0         | 50            |
    | Take money  | 50            | 10        | 40            |
    | Take money  | 50            | 20        | 30            |
    | Take money  | 50            | 30        | 20            |

  Scenario: Overdrafts are not allowed
    Given I have "50" euro
    When I select "Take money" from "Operation"
    And I fill in "Amount" with "60"
    And I press "Go"
    Then I should see "You have 50 euro on your account"
    And I should see "Overdrafts are not allowed"

Nous voilà prêts :-) Notez que les expressions utilisées (I fill in "xxx" with "xx", etc) ne sont pas anodines, bien au contraire. On va le voir par la suite.

Installer Mink

Rien de plus simple :

sudo pear install behat/mink-beta

Maintenant on va "dire" à Behat que l'on souhaite utiliser Mink pour nos tests. Ouvrez le fichier bootstrap/FeatureContext.php, et remplacez

class FeatureContext extends BehatContext {

par

require_once 'mink/autoload.php';
class FeatureContext extends Behat\Mink\Behat\Context\MinkContext

C'est là que la magie s'opère : Mink dispose déjà de nombreuses expressions disponibles pour tester notre produit web ! On va pouvoir le confirmer en faisant un :

behat -dl --lang=en
Résultat de la commande behat -dl après avoir installé Mink

Il ne nous reste plus qu'à préparer un petit fichier de configuration, nommé feature/behat.yml, qui sera automatiquement lu par behat :

<p>default:</p>
  context:
    parameters:
      base_url: http://localhost/mettez/ici/l/adresse/a/tester

Ici nous indiquons simpelment à Mink la racine de base de notre application, pour éviter de la répéter dans chacun de nos tests.

Tester le produit : Mink crée du sens

Il est temps de se lancer et d'exécuter en ligne de commande :

behat

Miracle ^^ : la majorité des expressions utilisées par notre product owner possède déjà un sens. En tant que développeur, il n'y a que deux expressions pour lesquelles je dois donner du sens :

Mink : résultat de la commande 1 : il reste des étapes à définir.

Quand le product owner spécifie 'I should see "content"', Mink va tout seul faire le lien avec l'assertion "le contenu de la page doit comporter le texte "content".

Mais bien mieux encore : les champs de mes formulaires ne sont pas reconnus par leur nom ou leur identifiant (name ou id). Non non, le product owner n'en n'a rien à faire de la structure interne d'une page web ; ce qu'il veut c'est un produit qui à l'écran correspond à ses attentes. Les éléments de formulaires sont reconnus par leur libellé (label).

le product owner n'en a rien à faire de la structure interne d'une page web ; ce qu'il veut c'est un produit qui, à l'écran, correspond à ses attentes

Bien sûr, Mink est permissif et on peut tout même cibler un lien, bouton, élément de formulaire... par son identifiant ou son nom... peu importe :-)

Vous trouverez la liste des expressions de base de Mink ici.

Donner son sens aux expressions non définies

Il nous reste donc deux expressions qui n'ont actuellement pas de signification : "Given I am logged in as ..." et "I have x euro". A nous, développeurs, de les définir dans features/bootstrap/FeatureContext.php.

Attention, il y a là un énorme piège. Je l'ai vu à chaque fois, le premier réflexe d'un développeur va être de créer un lien entre le code source de l'application et le code disponible dans le fichier FeatureContext.php. Par exemple en créer une instance d'une Zend_Application comme on le ferait pour des tests unitaires d'une application Zend... C'est la dernière chose à faire !

Non, si le product owner veut un produit web, si Mink nous permet de tester un produit web, continuons de tester un produit web uniquement, et laissons le code de côté :-p .

Le product owner nous dit qu'il est un utilisateur connecté ("I am logged in as..."). Connectons l'utilisateur au sein de l'application :

/**
 * @Given /^I am logged in as "([^"]*)"$/
 */
public function iAmLoggedInAs($username)
{
    return array(
        new Step\Given('I go to "login.php"')
        ,new Step\When("I fill in \"My name\" with \"$username\"")
        ,new Step\When('I press "Login"')
    );
}

Pensez à ajouter au début du fichier :

use Behat\Behat\Context\Step;

Comme vous le voyez, on réutilise les étapes utilisables directement dans nos scénarios. Dans 99% des cas cela suffit. Ce sont les mêmes étapes que le product owner pourrait utiliser dans un fichier de fonctionnalité classique.

Ce qui reviendrait à écrire :

Given I go to "login.php"
When I fill in "My name" with "jeanfrancois"
When I press "Login"

On fait la même chose pour donner du sens à 'I have "50" euro' :

/**
 * @Given /^I have "([^"]*)" euro$/
 */
public function iHaveEuro($balance) {
    return array(
        new Step\Given('I go to "/"')
        , new Step\When("I fill in \"New balance\" with \"$balance\"")
        , new Step\When('I press "Reset"')
    );
}

Ca y est, on est bons :

Mink : le produit est conforme

Le système de test est fiable car il n'est pas assujetti à des modifications de notre code, mais seulement aux modifications de notre produit. Si l'application venait à ne plus correspondre aux souhaits du product owner, celui-ci en serait immédiatement averti.

Un vrai navigateur, avec du vrai javascript

C'est beau, mais pas très réaliste : quid des applications riches ? Que faire si on a du Javascript et de l'ajax de tous les côtés ?

Et bien on va continuer d'utiliser Mink, mais cette fois-ci on va piloter un vrai navigateur. Mink permet de piloter n'importe quel navigateur en utilisant le driver de notre choix : Sahi ou Selenium (1 ou 2).

Ayant eu de nombreuses déconvenues avec Selenium par le passé, et n'ayant pas encore eu de souci majeur à ce jour avec Sahi, j'ai une nette préférence pour Sahi. Installons le ensemble :

L'instalaltion est simple. Téléchargez Sahi depuis http://sourceforge.net/projects/sahi/files/ et exécutez le :

wget http://sourceforge.net/projects/sahi/files/latest/download?source=files
<p>java -jar sahi_v35_20110719.jar</p>
Cliquez sur suivant, suivant... c'est pas moi qui vais vous apprendre un installer quelque chose :-D.

Lancez ensuite Sahi (placez vous bien dans le dossier spécifié pour éviter les ennuis) :

cd dossier/installation/bin/</p>
./sahi.sh &

Lier des fonctionnalités à un vrai navigateur

Pour utiliser un vrai navigateur à la place de l'émulateur, il suffit d'utiliser le tag @javascript devant le scénario qui le nécessite:

@javascript
  Scenario: ...

ou devant la fonctionnalité toute entière :

@javascript
<p>Feature: ...</p>

Ensuite on lance behat comme d'habitude :

behat

Vous devriez à ce stade voir se lancer votre navigateur et voir les pages changer toutes seules : les champs se remplissent, ça clique... Bref, ça marche !

Alors bien sûr vous allez me dire : c'est lent ! Et alors ? Ca reste toujours infiniment plus rapide que de tester tout ça à la main, et surtout c'est fiable : si une ligne est rouge, le produit n'est pas livrable ; mais si tout est vert, alors c'est que vous êtes dans les clous et que vous avez bien fait votre boulot. De plus le gain de temps de test manuel et d'aller et retours avec le product owner est considérable. Là c'est vraiment le client qui est le maître de son produit.

Configurer le navigateur

Il nous reste deux ou trois petites choses à voir. En effet, figurez vous que c'est firefox qui est lancé pour les tests, mais que moi je préfererai que ce soit chrome. Pas de problème, modifions le fichier behat.yml :

default:
  context:
    parameters:
      base_url: base_url: http://localhost/mettez/ici/l/adresse/a/tester
      browser: chrome

Il y a pas mal d'options, je vous laisse consulter la petite cheat sheet si besoin ;-)

La prochaine fois on verra comment allez plus loin dans le contrôle du navigateur, en exploitant l'API de Mink, et comment s'organiser pour travailler à plusieurs en créant des sous-contextes personnalisés plutôt que de ranger tout notre code dans un seul fichier.

Mais en attendant, n'hésitez pas à tester Behat et Mink et surtout à dire ce qu'il en est pour vous :-)

blog comments powered by Disqus