Intégration continue - PHP, Selenium et Xvfb - Partie 1
Par Metal3d le samedi, mars 31 2012, 11:23 - Développement - Lien permanent
Depuis quelques mois je me suis penché sur les outils d'intégration continue pour les projets PHP. Il en existe un bon paquet, mais celui qui pour le moment répond le mieux à mes attentes reste phpUnderControl. L'installation reste tout de même assez complexe et il me parait bon de vous montrer comment mettre en oeuvre ce genre d'outil. Le but étant de vous aider pas à pas à installer les outils qui permettront d'utiliser un service d'intégration continue. Nous allons passer en revue les outils à installer depuis PEAR, puis Selenium et enfin comment intégrer tout ça dans phpUnderControl.
Certes, je ne vais pas vous donner "la solution dans les règles de l'art" car chaque projet est différent, chaque attente est spécifique... Mais vous pourrez adapter au besoin les explications.
Nous allons donc procéder à ces opérations:
- installer les outils pear: phpcs, phpmd, phpcpd, phpunit
- installer selenium IDE avec le plugin php (ces outils ne sont pas obligatoires mais rendent la tâche tellement plus simple...)
- préparer un environnement X virtuel pour lancer les tests selenium
- installer phpundercontrol
- configurer la bête...
Les deux derniers points seront dans la partie 2, pour le moment il faut s'assurer d'avoir tous les composants nécessaires à la bonne conduite du projet.
En ce qui concerne Selenium et phpUnderControl, ce sont des outils développés en Java. On nous recommande d'utiliser le jre de oracle (comme ce n'est plus Sun...) mais pour ma part j'utilise openjdk sans aucun souci... alors pourquoi passer au propriétaire quand le libre fonctionne très bien ?
Donc, sur votre Fedora ou Centos, préparons un environnement propre... Je vous conseille d'utiliser un conteneur LXC pour bosser, mais si vous voulez utiliser votre système hôte je ne vais pas vous en empêcher.
On commence par installer les outils important:
- php-cli, php-pear
- openjdk
on ouvre un terminal, on se met en root et on installe tout ça
su - yum install php-cli php-pear java-1.7.0-openjdk
Pour la suite, je sais que certains d'entres vous vont me taper sur les
doigts, clamant haut et fort que les dépots Fedora ont les paquets PEAR
demandés (ou pas...) mais je vais être très clair: les paquets sur les serveurs
PEAR sont à jour, et certaines dépendances risquent de ne pas marcher si vous
utiliser les RPM. C'est le cas aussi sur Debian (j'en ai fait les frais cette
semaine). Donc ayez confiance et faites ce que je dis
On commence par faire deux ou trois manipulations sur PEAR pour être à l'aise (toujours en tant que root):
pear set-config auto_discover 1 pear upgrade pear pear install --alldeps pear.phpunit.de/PHPUnit pear install --alldeps phpunit/PHPUnit_Selenium pear install --alldeps pear.phpunit.de/phpcpd pear install --alldeps phpmd/PHP_PMD pear install --alldeps PHP_CodeSniffer pear install --alldeps channel://pear.phpdoc.org/PhpDocumentor-2.0.0a1
Si jamais il vous manque des paquets php, installez les, faites un "prear uninstall" du paquet qui a donné une erreur, et relancez l'installation du paquet.
Vous avez donc à présent:
- phpunit pour faire des tests unitaires de vos développement php
- phpunit-selenium qui est une classe permettant de piloter le serveur selenium qui lancera un navigateur pour tester des aspect fonctionnels
- phpcpd (Copy Paste Detector) qui vérifie les "copier coller" de code
- phpmd (Mess Detector) qui va vérifier des aspects complexe de code (variables non utilisé, code complexe, nom de variables ou fonctions...)
- phpcs qui va vérifier si votre code est bien formaté selon des standards choisis
- phpdocumentor qui permet de créer la documentation de vos projets
Cela étant fait, nous allons passer à la suite... Selenium !
Selenium est un ensemble d'outils qui permettent de piloter un navigateur au travers un serveur. Vous allez pouvoir vérifier si une page apparait correctement, si les liens sont bien présents sur une certaine page, et bien plus encore. Le principe est très simple:
- on enregistre une séquence de manipulations et de tests
- on l'exporte pour phpunit
- on lance le test
Mais pour que cela fonctionne, il faut jouer un peu dans le terminal... rien de bien méchant mais important.
Dans votre terminal, en root, téléchargez le serveur:
mkdir /opt/selenium cd /opt/selenium wget http://selenium.googlecode.com/files/selenium-server-standalone-2.20.0.jar
Ce serveur, une fois lancé, pilotera un firefox tout seul. Sauf que voilà... si vous travaillez sur une machine de bureau alors aucun souci ne se profile... mais quand on travaille sur un serveur distant... sans écran... ça va aller mal !
La solution la plus simple, selon moi, est d'installer un firefox "standalone" sur /opt/selenium et de lancer tout ça dans un Xvfb (X virtuel). On va donc faire cela... toujours dans votre répertoire /opt/selenium:
wget "http://download.mozilla.org/?product=firefox-11.0&os=linux&lang=fr" tar jxf firefox*.bz2
Si tout est ok, vous avez un répertoire "firefox" dans /opt/selenium
On va tenter de lancer firefox dans un X virtuel pour être certain que tout se passe bien:
Xvfb :99 & DISPLAY=:99 /opt/selenium/firefox/firefox & DISPLAY=:99 import -window root /tmp/snapshot.png
Maintenant, ouvrez /tmp/snapshot.png et vérifiez que vous voyez bel et bien un firefox ouvert. Si oui: nickel ! si non... heu vérifiez si il ne manque pas une librairie pour firefox (genre gdlib-dbus...)
Bref, si Xvfb et firefox ont marché alors on coupe:
killall Xvfb
Notez qu'on aurait put utiliser "xvb-run /opt/selenium/firefox/firefox" et utiliser le port retourné pour faire la capture, mais les lignes su-citées me paraissent plus explicites pour comprendre comment ça fonctionne.
Bref, utilisons maintenant le serveur selenium pour lancer un test.
Xvfb :99 & sleep 2 PATH=/opt/selenium/firefox:$PATH DISPLAY=:99 java -jar selenium-server-standalone-2.20.0.jar &
Attendez un peu...il faut voir une ligne indiquant que le serveur tourne, ce genre de ligne:
INFO org.openqa.jetty.http.SocketListener - Started SocketListener on 0.0.0.0:4444 INFO org.openqa.jetty.util.Container - Started org.openqa.jetty.jetty.Server@1ff4689e
Cela peut prendre entre 5 et 30 secondes, soyez patient... A partir de là, le serveur écoute le port 4444, on va donc jouer un peu !
Créer un fichier test.php:
<?php class Example extends PHPUnit_Extensions_SeleniumTestCase { protected function setUp() { $this->setBrowser("*chrome"); $this->setBrowserUrl("http://www.google.fr/"); } public function testMyTestCase() { $this->open("/"); $this->waitForPageToLoad("30000"); //on vérifie que "Google" est sur la page... $this->verifyTextPresent("Google"); //ce teste doit donner une erreur $this->verifyTextPresent("foo bar baz"); } }
Ce teste ne fait rien de compliqué... il ouvre Google et vérifie si le texte "Google" est sur la page...
Vous pouvez lancer le test:
phpunit test.php
Le résultat doit être:
PHPUnit 3.6.10 by Sebastian Bergmann. F Time: 5 seconds, Memory: 5.75Mb There was 1 failure: 1) Example::testMyTestCase foo bar baz Failed asserting that false is true. FAILURES! Tests: 1, Assertions: 2, Failures: 1.
Cela indique donc que le serveur selenium a bien lancé firefox, la page google et vérifier qu'un texte "Google" apparait. En ce qui concerne "foo bar baz" cela donne une erreur. Donc:
- 1 test
- 2 assertions : texte "Google" et texte "foo bar baz"
- 1 erreur, et une seule erreur !!! si vous en avez 2 alors c'est que cela n'a pas marché !
Voilà, on peut couper selenium et Xvfb:
killall Xvfb kill $(ps ax | grep selenium | grep -v grep | awk '{print $1}')
Maintenant, on se crée un service pour lancer Xvfb et Selenium.
Avant tout, pour des raison pratiques, je vous conseille de ne *plus du tout* lancer Xvfb et Selenium en root, mais avec un utilisateur précis. Comme nous allons utiliser phpUnderControl qui est un plugin de cruisecontrol, et que ce dernier lancera son service avec un utilisateur recommandé, nous allons le créer de suite et l'utiliser pour lancer Xvfb et Selenium.
useradd -m -s /bin/bash cruisecontrol
Notez ici que je demande la création du répertoire personnel (via l'option -m) car firefox aura besoin de créer un répertoire de profil, de ce fait, /home/cruisecontrol va être créé.
Voilà, maintenant créons le service qui lance Xvfb et Selenium via cet utilisateur. Créer le fichier /etc/init.d/xselenium et déposez ce code:
#!/bin/bash XLOCK="/var/lock/Xvfb.pid" SLOCK="/var/lock/selenium.pid" startXVFB(){ [[ -f $XLOCK ]] && echo "Already Running" && return 1 local PID PID=$(su cruisecontrol -c 'Xvfb :15 >/dev/null 2>&1 & echo $!') echo "Xvfb pid $PID" echo $PID > $XLOCK } startSelenium(){ [[ -f $SLOCK ]] && echo "Selenium Already Running" && return 1 local PID PID=$(su cruisecontrol -c 'PATH=/opt/selenium/firefox:$PATH DISPLAY=:15 java -jar /opt/selenium/selenium-server-standalone-2.20.0.jar >/dev/null 2>&1 & echo $!') echo "Selenium pid $PID" echo $PID > $SLOCK } case $1 in start) echo "Starting XVFB" startXVFB sleep 2 echo "Starting Selenium server" startSelenium sleep 2 ;; stop) kill $(cat $SLOCK) sleep 2 kill $(cat $XLOCK) sleep 2 rm -f $SLOCK $XLOCK ;; *) echo "$0 start|stop" ;; esac
Rendez le executable:
chmod +x /etc/init.d/xselenium
Et lancez le service:
/etc/init.d.xselenium start
Vous devez donc avoir désormais un Xvfb et un selenium actif. Attendez un peu que le port 4444 soit ouvert pour commencer à lancer les tests, pour vérifier le port:
netstat -taupen | grep 4444
Si aucune ligne n'apparait, c'est que le service ne tourne pas.
Voilà pour la partie pear, selenium-server et Xvfb. Dans la partie 2 nous utiliserons Selenium IDE pour créer des tests, mais surtout nous installerons phpUnderControl afin de piloter notre intégration continue.
En espérant avoir donner quelques clefs...