Contactez-nous

Kitpages
17 rue de la Frise
38000 Grenoble
tel : 04 58 00 33 81

Par Zazou Dernière mise à jour : 17 décembre 2013

Des tests fonctionnels dans Symfony 2 grâce à Behat et Mink

Introduction

Ce tutoriel est réalisé avec la v2.3.7 de Symfony

Toutes les commandes sont exécutées depuis la racine du projet.

Installer les vendors

Ajouter dans le composer.json puis exécuter un composer update

"require-dev": {
        "behat/behat": "2.5.1",
        "behat/symfony2-extension": "v1.1.0",
        "behat/mink": "v1.5.0",
        "behat/mink-extension": "v1.2.0",
        "behat/mink-browserkit-driver": "v1.1.0",
        "behat/mink-zombie-driver": "dev-master"
    }

Configuration

Créer le fichier behat.yml à la racine du projet, contenant le code suivant

  • base_url : adresse d'accès à l'environnement symfony de test, (créer le fichier app_test.php qui va bien à partir du app_dev.php -  doc officielle)
default:
  extensions:
    Behat\Symfony2Extension\Extension:
      mink_driver: true
    Behat\MinkExtension\Extension:
      default_session: 'symfony2'
      base_url: http://4000m.test/

Initialiser les features

Avec la commande suivante, on initialise le répertoire des futures features pour un bundle donné. (ici, DemoBundle de l'application Acme)

# pour initialiser les tests d'une appli complète
bin/behat --init

# pour initialiser les tests dans un bundle
bin/behat --init @AcmeDemoBundle

Lancer les tests

# Pour lancer les tests de l'appli complète si vous n'avez pas initialiser dans les bundles
./bin/behat

# Pour lancer les tests d'un bundle en particulier
./bin/behat @AcmeDemoBundle

# Pour lancer les tests de tous le projet
./script/behat.sh

Le fichier behat.sh

#!/usr/bin/env bash

NORMAL="\\033[0;39m"
BLEU="\\033[1;34m"

__DIR__="$(cd "$(dirname "${0}")"; echo $(pwd))"
__FILE__="${__DIR__}/$(basename "${0}")"
__ROOTDIR__=$(dirname ${__DIR__})
__APPDIR__=$(dirname ${__DIR__})/app
__SRCDIR__=$(dirname ${__DIR__})/src

BEHAT="./bin/behat"
APPCONSOLE="./app/console"

question() {
    local question=$1
    local choices=$2
    local default=$3
    echo -en "\n" "$BLEU" "== $question (${choices}) [${default}] " "$NORMAL"
    read RESPONSE
    if [ "x$RESPONSE" = "x" ]; then
        RESPONSE=$default
    fi
}

question "Supprimer et recréer la base de données ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    question "WARNING : Êtes-vous sûr de vouloir supprimer et recréer la base de données ?" "yes|no" "no"
    if [ "x$RESPONSE" = "xyes" ]; then
        $APPCONSOLE doctrine:database:drop --force --env=test
        $APPCONSOLE doctrine:database:create --env=test
    fi
fi

question "Clôner la bdd de dev pour les tests ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    echo -en "---> db host [127.0.0.1] : "
    read DB_HOST
    if [ "x$DB_HOST" = "x" ]; then
        DB_HOST="127.0.0.1"
    fi

    echo -en "---> db user [root] : "
    read DB_USER
    if [ "x$DB_USER" = "x" ]; then
        DB_USER="root"
    fi

    echo -en "---> db password [] : "
    read -s DB_PASS
    if [ "x$DB_PASS" = "x" ]; then
        DB_PASS=""
    fi

    echo -en "---> db name src [appsandbox_dev] : "
    read DB_NAME_SRC
    if [ "x$DB_NAME_SRC" = "x" ]; then
        DB_NAME_SRC="appsandbox_dev"
    fi

    echo -en "---> db name dest [appsandbox_test] : "
    read DB_NAME_DEST
    if [ "x$DB_NAME_DEST" = "x" ]; then
        DB_NAME_DEST="appsandbox_test"
    fi

    mysqldump -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME_SRC | mysql -h$DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME_DEST
fi

question "Vérifier la validité du schéma ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    $APPCONSOLE doctrine:schema:validate --env=test
fi

question "Consulter les modifications du schéma avant la mise à jour ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    $APPCONSOLE doctrine:schema:update --env=test --dump-sql --complete
fi

question "Mettre à jour le schéma ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    $APPCONSOLE doctrine:schema:update --env=test --force --complete
fi

question "Ajouter des données depuis les fixtures doctrine ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    $APPCONSOLE doctrine:fixtures:load --append --env=test
fi

question "Créer un compte fos admin ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    echo -en "---> admin login [admin] : "
    read ADMIN_LOGIN
    if [ "x$ADMIN_LOGIN" = "x" ]; then
        ADMIN_LOGIN="admin"
    fi
    echo -en "---> admin email [test@hotmail.com] : "
    read ADMIN_EMAIL
    if [ "x$ADMIN_EMAIL" = "x" ]; then
        ADMIN_EMAIL="test@hotmail.com"
    fi
    echo -en "---> admin password [] : "
    read ADMIN_PASSWORD
    if [ "x$ADMIN_PASSWORD" = "x" ]; then
        echo "the admin password is mandatory";
        exit;
    fi
    $APPCONSOLE fos:user:create $ADMIN_LOGIN $ADMIN_EMAIL $ADMIN_PASSWORD  -env=test
    $APPCONSOLE fos:user:promote $ADMIN_LOGIN ROLE_ADMIN --env=test
fi

question "Insérer les uploads depuis l'archive tar.gz ?" "y|n" "n"
if [ "x$RESPONSE" = "xy" ]; then
    cd ./web
    tar zxvf ../app/Resources/docs/uploads.tar.gz
    cd -
fi

question "Lancer tous les tests en une fois ?" "y|n" "y"
if [ "x$RESPONSE" = "xy" ]; then
    for app in `ls $__SRCDIR__`; do
        for bundle in `ls $__SRCDIR__/$app`; do
            if [ -d $__SRCDIR__/$app/$bundle/Features ]; then
                $BEHAT @$app$bundle
            fi;
        done;
    done;
else
    for app in `ls $__SRCDIR__`; do
        for bundle in `ls $__SRCDIR__/$app`; do
            if [ -d $__SRCDIR__/$app/$bundle/Features ]; then
                question "Lancer les tests de @$app$bundle ?" "y|n" "n"
                if [ "x$RESPONSE" = "xy" ]; then
                    $BEHAT @$app$bundle
                fi
            fi
        done;
    done;
fi

echo -e "\nTerminé !\n"

Ecrire des tests

Pour écrire des scénarios web, il ne faut pas oublier de modifier la classe FeatureContext pour qu'elle étende le MinkContext à la place de BehatContex. Cette modification nous fera gagner du temps en donnant du sens à des phrases clés pour les étapes. (cf. Liens utiles)

Ci-dessous, un exemple simple d'un test web qui ne nécessitera aucun code supplémentaire.

Feature: Administration des réservations
  Dans le but de gérer les réservations en tant qu'administrateur

  Scenario: Un utilisateur peut se connecter à l'administration
    Given I am on homepage
    When I follow "login"
      And I fill in "Zazou" for "Nom d'utilisateur"
      And I fill in "123456" for "Mot de passe"
      And I press "Connexion"
    Then I should be on "/admin/catalogue/dropzone"

Donner du sens aux tests

Pour donner du sens au code écrit dans les tests et qui sortent du cadre de Mink, il faut alors coder les méthodes qui vont bien dans le FeatureContext.php correspondant. Petite astuce, pour accéder au container Symfony, il suffit de rajouter le trait KernelDictionary. Le container devient alors accessible depuis $this->getContainer(). Et par extension, on a accès aux services symfony.

<?php

// namespace ...

use Behat\Symfony2Extension\Context\KernelDictionary;
// ...

class FeatureContext extends MinkContext implements KernelAwareInterface
{
    use KernelDictionary;

    // ...

    /**
     * @Then /^an order was registered in database with status "([^"]*)"$/
     */
    public function anOrderWasRegisteredInDatabaseWithStatus($arg1)
    {
        $container = $this->getContainer();
        $doctrine = $this->getContainer()->get('doctrine');

        // ...
    }
}

Commentaires

Ajouter un commentaire