Behat + Mink + Selenium

Hier soir, un post sur l’utilisation de Selenium en mode Grid. Le script de test était en Python.

Ce soir, on va passer par Behat + Mink : ce duo forme un framework pour le développement par les tests.

Installation de behat et Mink

J’utilise la méthode avec composer en installant Behat et Mink dans mon répertoire projet. On commence par écrire le fichier composer.json , voilà le mien

{
    "require": {
        "behat/behat": "2.4.*@stable",
        "behat/mink": "1.4.*@stable",
        "behat/mink-extension": "*",
        "behat/mink-goutte-driver": "*",
        "behat/mink-selenium2-driver": "*",
        "sanpi/behatch-contexts": "*",
        "behat/gherkin": "*",
        "phpunit/phpunit": "3.7.*"
    },
    "minimum-stability": "dev",
    "config": {
        "bin-dir": "bin/"
    }
}

On récupère composer

curl http://getcomposer.org/installer | php

Et on lance l’installation

php composer.phar install

Création de notre premier tests

On va reprendre l’exemple d’hier à savoir le test d’une recherche google. On commence par écrire un fichier behat.yml, ce fichier va contenir la configuration de nos tests comme le browser à utiliser, l’adresse du serveur selenium … Un exemple de fichier :

# behat.yml
default:
    extensions:
        Behat\MinkExtension\Extension:
            base_url: http://www.google.fr
            goutte: ~
            default_session: selenium2
            javascript_session: selenium2
            selenium2:
                browser: 'firefox'
                capabilities: { "browserName": "firefox", 'platform':'LINUX', 'browserVersion':'','version':'' }
                wd_host: 'http://localhost:4444/wd/hub'

Ensuite on initialise le projet de test par la commande :

./bin/behat --init

On obtient en résultat la création d’un répertoire features qui va contenir nos tests, et justement, on va écrire dans ce répertoire un fichier test_google.feature avec la contenu suivant :

Feature: Test Google
    Test de google
 
    @javascript
    Scenario: Recherche selenium
        Given I am on "/"
        When I fill in "q" with "Selenium"
        And I press "btnG"
        Then  I should see "Selenium - Web Browser Automation"

Et voilà, un script simple à écrire et à comprendre. Vu que nous avons avec le post d’hier un grid de selenium, il suffit de lancer le test avec

./bin/behat

Behat va lancer les scripts du répertoire features. Et là, le drame, on a obtient un truc bizarre du genre :

1 scénario (1 non définies)
4 étapes (4 non définies)
0m0.007s

Vous pouvez implémenter les définitions d’étapes pour les étapes non définies avec ces modèles :

/**
 * @Given /^I am on "([^"]*)"$/
 */
public function iAmOn($arg1)
{
    throw new PendingException();
}

/**
 * @When /^I fill in "([^"]*)" with "([^"]*)"$/
 */
public function iFillInWith($arg1, $arg2)
{
    throw new PendingException();
}

/**
 * @Given /^I press "([^"]*)"$/
 */
public function iPress($arg1)
{
    throw new PendingException();
}

/**
 * @Then /^I should see "([^"]*)"$/
 */
public function iShouldSee($arg1)
{
    throw new PendingException();
}

En fait Behat analyse le scenario avec son parser et il a essayé d’exécuter les commandes en fonction de définitions présente dans un Context, lorsqu’il ne trouve pas les commandes dans son Context il vous donne les fonctions à coder pour étendre les définitions.

Première solution, on code les définitions. Deuxième solution, on change le Context pour utiliser celui de Mink qui dispose des définitions qui vont bien. On édite le fichier features/bootstrap/FeatureContext.php qui est le bootstrap qui initialise notre contexte (c’est aussi là, qu’on code nos nouvelles définitions)

Ajouter la ligne

use Behat\MinkExtension\Context\MinkContext;

et on remplace BehatContext par MinkContext. On relance le test et là, nickel firefox s’affiche et le test s’exécute. Mais encore un problème car lors du passage du test, selenium va trop vite ce qui ne laisse pas le temps de valider le test par détection de la chaine de caractères. Un méthode comme une autre est d’ajouter une tempo en ajoutant une étape pour faire une pause

On change le scénario du test en :

Feature: Test Google
    Test de google
 
    @javascript
    Scenario: Recherche selenium
        Given I am on "/"
        When I fill in "q" with "Selenium"
        And I press "btnG"
        And Je me repose un peu
        Then  I should see "Selenium - Web Browser Automation"

Là, ok la définition “Je me repose un peu” n’existe certainement pas dans MinkContext, on va l’ajouter dans notre FeatureContext.php A noter qu’en lançant :

./bin/behat --dry-run

Behat simule le test et surtout nous retourne le code à ajouter dans notre fichier, soit :

/**
 * @Given /^Je me repose un peu$/
 */
public function jeMeReposeUnPeu()
{
    throw new PendingException();
}

On colle cela dans notre fichier et on remplace l’exception par le code qui va bien :

/**
 * @Given /^Je me repose un peu$/
 */
public function jeMeReposeUnPeu()
{
    $this->getSession()->wait(1000);
}

Et on relance ….. Et voilà, un scénario complet qui se termine avec succès.

Bonus

Là, on a lancé le test sous firefox, mais comment mutualiser ce scénario pour tester soit sur firefox sous linux ou sous android. On réédite le fichier behat.yml pour définir 2 profiles, 1 pour firefox et 1 pour android, cela donne :

# behat.yml
default:
    extensions:
        Behat\MinkExtension\Extension:
            base_url: http://www.google.fr
            goutte: ~
            default_session: selenium2
            javascript_session: selenium2
            selenium2:
                browser: 'firefox'
                capabilities: { "browserName": "firefox", 'platform':'LINUX', 'browserVersion':'','version':'' }
                wd_host: 'http://localhost:4444/wd/hub'
 
android:
    filters:
        tags: @android
    extensions:
        Behat\MinkExtension\Extension:
            base_url: http://www.google.fr
            goutte: ~
            default_session: selenium2
            javascript_session: selenium2
            selenium2:
                browser: 'android'
                capabilities: { "browserName": "android", 'platform':'ANDROID', 'browserVersion':'','version':'' }
                wd_host: 'http://localhost:4444/wd/hub'
 
firefox:
    filters:
        tags: @firefox
    extensions:
        Behat\MinkExtension\Extension:
            base_url: http://www.google.fr
            goutte: ~
            default_session: selenium2
            javascript_session: selenium2
            selenium2:
                browser: 'firefox'
                capabilities: { "browserName": "firefox", 'platform':'LINUX', 'browserVersion':'','version':'' }
                wd_host: 'http://localhost:4444/wd/hub'

On a 2 profiles qui utilisent des filtres basé sur des tags. Si on lance le profile android, Behat va exécuter les scripts taggué @android et pour firefox …. Donc on ouvre notre scénario et on va les tagguer:

Feature: Test Google
    Test de google
 
    @javascript @firefox @android
    Scenario: Recherche selenium
        Given I am on "/"
        When I fill in "q" with "Selenium"
        And I press "btnG"
        And Je me repose un peu
        Then  I should see "Selenium - Web Browser Automation"

Super simple non ???? Et du coup, maintenant, pour lancer le scénario sous android, on lance :

./bin/behat --profile android

pour firefox

./bin/behat --profile firefox

Et voilà, par rapport au post d’hier, inutile de faire 2 scripts. Avec Behat, on rédige des scénario en langage naturel, et on tag en fonction des plate-formes. On lance Behat qui transmet à Selenium les tests à exécuter. Behat.yml permet de spécifier les plate-formes cibles en fonction du profile. Enfin, Behat permet de définir nos propres définition.

Bonus 2

Encore une info, on peut ajouter la ligne suivante :

# language: fr

ce qui permet d’avoir des scripts du genre :

Fonctionnalité: Test Google
    Test de google
 
    @javascript @firefox @android
    Scénario: Recherche selenium
        Soit je suis sur "/"
        Quand je remplis "q" avec "Selenium"
        Et je presse "btnG"
        Et Je me repose un peu
        Alors je devrais voir "Selenium - Web Browser Automation"

Si c’est pas cool ça … Mais attention, Behat est case sensitive

comments powered by Disqus