Contactez-nous
Kitpages
17 rue de la Frise
38000 Grenoble
tel : 04 58 00 33 81
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