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...