SPARQL Protocol and RDF Query Language/Travail pratique/Initiation à PHPUnit
Pourquoi s'initier à PHPUnit ?
modifierDans ce TP, nous allons installer PHPUnit, qui est particulièrement utile pour faire des tests de non-régression, ainsi que Phing, pour automatiser ces tests.
À savoir avant de commencer
modifierPHPUnit fournit un cadre simple pour créer une suite de tests pour automatiser les tests unitaires de fonctions et de classes.
PHPUnit a été inspiré par JUnit, qui a été créé par Kent Beck et Erich Gamma en tant qu'outil pour l’eXtreme programming (ou XP). Une des règles de XP consiste à tester les composants logiciels aussi souvent que possible et le plus tôt possible. De cette façon, vous n'aurez plus à corriger les bugs et erreurs de ces classes, tout en mettant en place une application plus importante qui dépend des mêmes classes.
Généralement, le développement des tests unitaires avec PHPUnit est à faire quel que soit le nombre de personnes sur un projet et on n'applique les autres règles XP qu’à partir de 4 ou 5 personnes développant sur le même projet.
Des tests unitaires font gagner beaucoup de temps dès le début d'un projet.
Les bibliothèques PHP pour SPARQL comportent des tests unitaires qu’il faut savoir exécuter pour pouvoir développer des applications avec SPARQL.
Documentations
modifier(vérifier version 3.5 ou 3.6)
Installation
modifierPHPUnit
modifierPHPUnit n’est pas toujours évident à installer. Il ne faut pas hésiter à chercher sur le Web les solutions que les utilisateurs ont trouvées pour mettre à jour leurs versions.
Nous installons les paquets :
- phpunit pour installer phpunit 3.4 que nous allons mettre à jour
- php5-curl, bibliothèque de communication réseau pour PHP, qui est utilisée par phpunit 3.5
Suivre les instructions suivantes pour installer PHPUnit :
1. Taper ces commandes :
Sous Ubuntu
sudo apt-get install phpunit php5-curl
sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover components.ez.no
sudo pear channel-discover pear.symfony-project.com
sudo pear upgrade-all
sudo pear install -f phpunit/PHPUnit
Sous Fedora
su -
yum install build-essential phpunit php5-curl
pear channel-discover pear.phpunit.de
pear channel-discover components.ez.no
pear channel-discover pear.symfony-project.com
pear upgrade-all
pear install -f phpunit/PHPUnit
2. Vérifier la version installée par PHPUnit avec la commande suivante :
phpunit --version
Ce TP a été fait avec la version 3.5.14. Il devrait être compatible avec toutes les versions 3.5.
Phing
modifierPhing est un outil qui va permettre de déployer notre projet, mais aussi d'exécuter nos tests unitaires avec PhpUnit.
Installer Phing avec ces commandes :
sudo pear channel-discover pear.phing.info
sudo pear install phing/phing
Il y a un problème d'encodage de la sortie pour la console d'Eclipse dans le TP suivant. Nous réglons immédiatement le problème.
Ouvrir le fichier suivant :
sudo vim /usr/bin/phing
Remplacer la ligne suivante :
$PHP_COMMAND -d html_errors=off -qC /usr/share/php/phing.php -logger phing.listener.AnsiColorLogger "$@"
Par cette ligne :
$PHP_COMMAND -d html_errors=off -qC /usr/share/php/phing.php -logger phing.listener.DefaultLogger "$@"
Développer avec PHPUnit
modifierLes débutants en PHP pensent, souvent à tort, qu’il suffit de faire quelques echo, print_r() ou var_dump() pour tester leurs programmes. Dès lors que quelqu’un développe de cette manière, il est très difficile de modifier cette habitude.
Les tests doivent accompagner toute la vie d'un logiciel. Si un développeur modifie un logiciel sans maintenir les tests associés, il brise la chaîne vertueuse que ses pères ont essayé de maintenir et fait du logiciel une version Beta pour toujours (c'est-à-dire une version boguée). Dès lors qu'un logiciel ne subit plus de tests, sa durée de vie sera courte.
De plus, la méthode agile (ou Extreme Programming) intègre les tests unitaires. Cela permet de modifier violemment le code pour l'améliorer (refactoring), car le développeur sait qu’il y a un filet de sécurité qui ne laissera passer aucune erreur déjà connue.
Voici la méthode pour développer des tests unitaires, quel que soit le langage :
- concevoir votre classe/fonction sans l'implémenter
- créer une suite de tests
- ajouter votre suite de tests dans le script de déploiement de votre logiciel (insérer dans votre "usine de développement" si c’est un projet à plus de 4 ou 5 personnes à plein temps)
- implémenter la classe
- exécuter le script de compilation avec les tests activés
- corriger les échecs ou les erreurs et relancer le script de compilation/tests
Après la mise en production du logiciel, les tests unitaires suivent la vie du logiciel :
- un client ou quelqu’un d’autre voit une erreur et vous la transmet
- vous tentez de reproduire l'erreur, en développant un nouveau test unitaire
- seulement après être arrivé à reproduire l'erreur, vous modifiez le programme pour la corriger
- vous re-testez ;
- si tout est conforme, déployer la nouvelle version.
Voici la théorie, mais on ne peut pas toujours reproduire une erreur immédiatement ; il faut alors improviser pour analyser le problème et trouver un palliatif à cette erreur.
Dans ce TP, nous n'utiliserons pas d'usine de développement ( comme Jenkins ou autres), car nous considérons que nous sommes seuls sur ce projet et que nous pouvons déployer le code sans aucune validation extérieure.
Nous développerons dans ce TP un simple script build.xml qui suffira largement à importer, tester et déployer nos exemples pour nos TP.
Concevoir l'arborescence de votre projet
modifierUn projet Web ne doit pas être développé alors qu’il est en même temps en production (c'est-à-dire en cours d'utilisation).
Dans le TP faire un serveur Web, nous avons créé un site Web, moncv, qui servira aux tests sur notre PC de développement avant un possible déploiement à distance sur un autre serveur.
Nous aurons donc :
- une zone de test pour l’application Web dans le répertoire
/var/www/moncv
; - un répertoire pour développer notre projet et faire nos tests unitaires, par exemple
~/projets/moncv
.
Dans ce répertoire de développement, nous retrouverons généralement :
- un fichier
README
qui indique comment déployer et tester le projet ; - un fichier
build.xml
qui contient les scripts pour déployer et la manière de le tester; - un répertoire
src
qui contient les sources que vous avez développées ; - un répertoire
tests
qui contient les tests des sources que vous avez développées ; - un répertoire
lib
qui contient les bibliothèques de classes PHP que vous ne développez pas dans ce projet (ou que vous n'avez pas développé vous-même) ; - un répertoire
bdd
qui contient les scripts SQL pour mettre à jour la base de données.
Exercice : Créer sur le répertoire ~/projets/moncv
: les sous-répertoires comme indiqué plus haut.
Voici les commandes à entrer sous Linux pour créer l'arborescence de votre projet moncv :
cd ~
mkdir projets
cd projets
mkdir moncv
cd moncv
mkdir src
mkdir tests
mkdir bdd
mkdir lib
On créera le fichier build.xml et README un peu plus loin dans le TP.
Concevoir votre classe
modifierNous supposons que nous avons besoin de faire une classe String.
Exercice :
- Créer le fichier String.class.php dans le répertoire ~/projets/moncv/src
- Insérer la classe ci-dessous dans ce fichier
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
}
// adds another string object to this class
function add($string) {
}
// returns the formated string
function toString($format) {
}
}
Créer des tests
modifierMaintenant, nous pouvons créer une suite de tests qui vérifie chaque fonction de votre classe String.
Une suite de tests PHP est une classe qui :
- hérite de la classe PHPUnit_TestCase ;
- a des fonctions de tests identifiées par leur nom, commençant par le mot test.
Exemple : la fonction testToString() est une fonction de test, car commençant par "test".
Une fonction de test appelle des fonctions avec des paramètres pour obtenir une certaine valeur. Si la valeur obtenue est différente de la valeur attendue, la fonction de test génère une erreur qui fera échouer la suite de tests.
Exercice : Créer une suite de tests de la classe String
1. Créer dans le répertoire tests du projet la classe String.test.php.
2. Insérer le texte ci-dessous.
<?php
require_once dirname(__FILE__).'/../src/String.class.php';
// require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/Autoload.php';
class StringTest extends PHPUnit_Framework_TestCase
{
// contains the object handle of the string class
var $abc;
// constructor of the test suite
function StringTest() {
}
// called before the test functions will be executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function setUp() {
// create a new instance of String with the
// string 'abc'
$this->abc = new String("abc");
}
// called after the test functions are executed
// this function is defined in PHPUnit_TestCase and overwritten
// here
function tearDown() {
// delete your instance
unset($this->abc);
}
// test the toString function
function testToString() {
$result = $this->abc->toString('contains %s');
$expected = 'contains abc';
$this->assertTrue($result == $expected);
}
// test the copy function
function testCopy() {
$abc2 = $this->abc->copy();
$this->assertEquals($abc2, $this->abc);
}
// test the add function
function testAdd() {
$abc2 = new String('123');
$this->abc->add($abc2);
$result = $this->abc->toString("%s");
$expected = "abc123";
$this->assertTrue($result == $expected);
}
}
Créer la suite de tests
modifierNous devons, dans un premier temps, agréger les tests dans une classe PHP que l’on nomme ClassTest, dans le répertoire "tests" du projet. Cela constituera notre suite de tests.
Exercice : Créer le fichier ClassTest.php dans le répertoire tests, qui contiendra le texte ci-dessous.
<?php
//require_once 'PHPUnit/Framework.php'; // non existent
require_once 'PHPUnit/Autoload.php';
require_once 'String.test.php';
class ClassTest extends PHPUnit_Framework_TestSuite
{
public static function suite()
{
$suite = new ClassTest('Class Test');
$suite->addTestSuite('StringTest');
return $suite;
}
protected function setUp()
{
print "\nClassTest::setUp()\n";
}
protected function tearDown()
{
print "\nClassTest::tearDown()\n";
}
}
Une fois que nos suites de tests sont créées (ici, en réalité, nous n'avons qu'une suite), nous avons une classe pour agréger ces suites et donc tous les tests du projet.
Exercice : Créer le fichier AllTests.php dans le répertoire tests qui contiendra le texte ci-dessous.
<?php
//require_once 'PHPUnit/Framework.php'; // non existent
require_once 'PHPUnit/Autoload.php';
require_once 'ClassTest.php';
class AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('Project');
$suite->addTest(ClassTest::suite());
return $suite;
}
}
On exécute les tests avec la ligne de commande suivante :
phpunit --colors --verbose tests/AllTests.php
Cela nous donne ce résultat :
FAILURES!
Tests: 3, Assertions: 3, Failures: 3
Les tests indiquent qu’ils ont échoué. C'est logique, car la classe String n'a pas encore été implémentée.
Créer le script de déploiement
modifierOn crée maintenant le script qui nous permettra de déployer le projet et de tester nos classes. Ce fichier sera progressivement complété dans les autres TP.
Pour le moment, il contiendra seulement les lignes suivantes :
<?xml version="1.0" encoding="UTF-8"?>
<project name="MonCV" default="build" basedir=".">
<property name="src" value="./src"/>
<property name="tests" value="./tests" />
<property name="deploy_web" value="/var/www/moncv" />
<target name="deploy" description="Copies files to web .">
<echo message="Running build.xml. Copying files from dev to web..." />
<copy todir="${deploy_web}">
<fileset dir="${src}">
<include name="**/*.php" />
<include name="**/*.htm" />
</fileset>
</copy>
</target>
<target name="test">
<exec
passthru="true"
command="phpunit ${tests}/AllTests.php"
checkreturn="true" />
</target>
<target name="build" depends="test,deploy"/>
</project>
Exercice : Créer le fichier build.xml à la racine du projet moncv ; il contiendra le texte ci-dessus.
On exécutera maintenant les tests avec la ligne de commande suivante :
phing test
On peut lancer les tests et le déploiement directement avec cette commande :
phing
Créer le fichier README
modifierOn crée pour finir le fichier README pour décrire notre programme et la manière de le tester.
Normalement le fichier README contient aussi le type de licence du programme, le nom du développeur, etc.
Exercice : Créer le fichier README à la racine du projet moncv qui contiendra le texte ci-dessous.
== About ==
TP SPARQL
== Howto TESTS ==
ToDo : phing test
Implémenter et tester
modifierNous implémentons la classe String.class.php :
<?php
class String
{
//contains the internal data
var $data;
// constructor
function String($data) {
$this->data = $data;
}
// creates a deep copy of the string object
function copy() {
$ret = new String($this->data);
return $ret;
}
// adds another string object to this class
function add($string) {
$this->data = $this->data.$string->toString("%ss");
}
// returns the formated string
function toString($format) {
$ret = sprintf($format, $this->data);
return $ret;
}
}
Exercice : Tester à nouveau le projet avec la commande "phing test" et corriger l'erreur qui se trouve dans la classe String.
Il y a une erreur ligne 20. Voici la correction :
$this->data = $this->data.$string->toString("%s");
Vous savez maintenant faire des tests unitaires en PHP. Cela vous sera indispensable pour tester et modifier les bibliothèques PHP afin d’utiliser SPARQL.