SPARQL Protocol and RDF Query Language/Travail pratique/Initiation à PHPUnit

Début de la boite de navigation du travail pratique
Initiation à PHPUnit
Image logo représentative de la faculté
T.P. no 5
Leçon : SPARQL Protocol and RDF Query Language

TP de niveau 15.

Précédent :Initiation à MySQL
Suivant :Initiation à Eclipse
En raison de limitations techniques, la typographie souhaitable du titre, « Travail pratique : Initiation à PHPUnit
SPARQL Protocol and RDF Query Language/Travail pratique/Initiation à PHPUnit
 », n'a pu être restituée correctement ci-dessus.


Pourquoi s'initier à PHPUnit ?

modifier

Dans 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

modifier

PHPUnit 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

modifier

PHPUnit

modifier

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

modifier

Les 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 :

  1. concevoir votre classe/fonction sans l'implémenter
  2. créer une suite de tests
  3. 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)
  4. implémenter la classe
  5. exécuter le script de compilation avec les tests activés
  6. 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 :

  1. un client ou quelqu’un d’autre voit une erreur et vous la transmet
  2. vous tentez de reproduire l'erreur, en développant un nouveau test unitaire
  3. seulement après être arrivé à reproduire l'erreur, vous modifiez le programme pour la corriger
  4. vous re-testez ;
  5. 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

modifier

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

On créera le fichier build.xml et README un peu plus loin dans le TP.

Concevoir votre classe

modifier

Nous supposons que nous avons besoin de faire une classe String.

Exercice :

  1. Créer le fichier String.class.php dans le répertoire ~/projets/moncv/src
  2. 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

modifier

Maintenant, 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

modifier

Nous 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

modifier

On 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

modifier

On crée pour finir le fichier README pour décrire notre programme et la manière de le tester.


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

modifier

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

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.