Tests Unitaires
Les tests unitaires ont pour but d'évaluer les parties (ou unités) individuelles du programme (fonctions et méthodes), afin de s'assurer que chacune de ces parties effectue, isolément, la tâche qu'elle est censée effectuer. Ces test, qui constituent l'une des bases de l'Extreme Programming, est particulièrement indiquée pour un logiciel Open Source, la dispertion géographique des collaborateurs les obligeant à être particulièrement méticuleux sur le détail des tests. Ils constituent donc un jalon particulièrement souhaitable des premières étapes d'un processus d'assurance qualité pour Moodle.
Les étapes suivantes de ce processus d'assurance qualité inclueront des tests d'intégration, afin de s'assurer que les différentes unités individuelles travailleront efficacement ensemble.
Le cadre (framework en anglais) des unités de test, dans Moodle, est basé sur celui de SimpleTest. Il a été incorporé à Moodle par Nick Freear et Tim Hunt de la Open University.
Logiciel Open Source créé par Marcus Baker, un consultant de Londres (Royaume-Uni), Simple Test est un cadre de test unitaire pour le langage PHP, dont la structure de test est inspirée par celles de JUnit et de PHPUnit.
Exécuter les tests unitaires dans Moodle
Exécuter les tests de base
- Accédez à Moodle avec un compte administrateur.
- Choisissez l'écran d'administrateur.
- Cliquez sur le lien des Reports, tout près du bas de la page.
- Cliquez sur le lien Run the unit tests.
- Il ne reste qu'à attendre le résultat des tests.
Options pour l'exécution des tests
Au bas de la page de test, un formulaire vous permet de choisir des options pour l'exécution des tests.
Show passes as well as fails
Par défaut, seuls les détails des tests qui ont échoué sont affichés. Cette option permet d'afficher également les détails des tests qui ont réussi.
Show the search for test files
Les tests devant être exécutés sont repérés automatiquement en recherchant les fichiers dont le nom correspond à la formule test*.php ("*" indiquant une chaîne de caractères d'une longueur indéfinie, incluant 0). Cette option permet d'afficher la liste des dossiers explorés dans le cadre de cette recherche, ainsi que les tests qui ont été trouvés.
Cette option est très utile dans le cadre d'un débogage, et tout particulièrement celui d'une erreur de syntaxe, car, comme chacun sait, ce genre d'erreur, parfois, n'affiche qu'une page blanche.
On peut aussi activer cette option en inscrivant, manuellement, "showsearch=1" à la fin de l'URL.
Run a thorough test
L'exécution des tests unitaires après avoir activé cette option peut, selon les cas, être longue. Dans ce cas, en effet, en plus de repérer les fichiers dont le nom correspond à la formule test*.php, la recherche incluera les fichiers dont le nom correspond à la formule slowtest*.php.
Cette option procède à des tests très détaillés : elle n'est donc pas indiquée pour les tests de tous les jours sur le code en usage courant, mais plutôt dans le cadre d'un développement ou d'un débogage. Dans ce dernier cas, l'option est particulièrement utile lorsque combinée avec l'option suivante.
Only run tests in
Par défaut, les tests sont effectués dans le code de base en entier. Dans le cadre d'un développement ou d'un débogage dans une section restreinte du code, cela n'est pas nécessaire. L'option "Only run tests in" vous permet de restreindre les tests à un dossier ou un fichier : il suffit de taper l'adresse du dossier ou du fichier.
Il existe un moyen encore plus simple de restreindre la portée des tests. Lorsque l'échec ou la réussite du test est affichée, chaque partie de l'adresse du fichier où se trouve le code est un lien permettant d'effectuer un test unitaire uniquement dans le dossier ou le fichier choisi.
Écrire de nouveaux tests
Voici un exemple où l'on rédige des tests pour le fichier 'question/editlib.php'.
Où mettre les tests
Comme il a été expliqué dans la section sur l'option "Show the search for test files", il vous faut créer un fichier destiné aux tests, et qui porterait un nom qui pourrait être testeditlib.php, rangé dans un dossier dont l'adresse pourrait être question/simpletest. Voici ce que pourrait être ce fichier :
<?php /** * Unit tests for (some of) question/editlib.php. * * @copyright © 2006 The Open University * @author T.J.Hunt@open.ac.uk * @license http://www.gnu.org/copyleft/gpl.html GNU Public License * @package question */ /** */ require_once(dirname(__FILE__) . '/../../config.php'); global $CFG; require_once($CFG->libdir . '/simpletestlib.php'); // Include the test libraries require_once($CFG->dirroot . '/question/editlib.php'); // Include the code to test /** This class contains the test cases for the functions in editlib.php. */ class question_editlib_test extends UnitTestCase { function test_get_default_question_category() { // Do the test here/ } } ?>
Structure de base d'un test
On peut résumer la structure de base d'un test par :
function test() { // Initialisations nécessaires aux tests. // Appel des fonctions à tester // Résultats. }
Méthodes d'initialisation et de disposition
Si tous vos cas de test appartiennent au même module, et s'ils partagent les mêmes données, vous pouvez créer une méthode appelée setUp()
initialisant les données. Lorsque cette méthode est présente, cette méthode sera appelée avant chaque test. De même, vous pouvez écrire une méthode tearDown()
si des opérations communes doivent être exécutées après chaque test.
Une bonne astuce pour accélérer vos test serait de les séparer en différents fichiers, selon les manoeuvres à effectuer avant et/ou après les tests.
Précautions à prendre pour certains types de code
Code exécuté dirèctement
Lors des tests unitaires, les codes sont testés à partir des librairies de SimpleTest. Cependant, si le code s’attend à être exécuté directement (par exemple index.php), plusieurs erreurs liées au fait que cette condition n’est pas respectée seront levées.
Chemins d'accès
Lorsque l'appel d'une fonction inclut un paramètre représentant un chemin d'accès à un fichier, sa vérification avec les tests unitaire provoquera une erreur.
Au lieu d'écrire :
require_once('../../config.php');
on écrira :
require_once(dirname(__FILE__) . '/../../config.php');
Accès aux variables globales
Évidemment, comme vous n'êtes plus dans le contexte de la fonction originale, une variable globale n'est plus disponible quand elle est nécessaire.
Au lieu d'écrire :
require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/moodlelib.php');
on écrira :
require_once(dirname(__FILE__) . '/../../config.php'); global $CFG; // You need this. require_once($CFG->libdir . '/moodlelib.php');
Tests unitaires dans la version 2.0
Avec l'objectification des librairies dans Moodle 2.0, de nouvelles approches plus simples peuvent être utilisées pour les tests unitaires. Voici quelques exemples de tests unitaires (dans course/simpletest) :
require_once($CFG->dirroot . '/course/lib.php'); global $DB; Mock::generate(get_class($DB), 'mockDB'); class courselib_test extends UnitTestCase { var $realDB; function setUp() { global $DB; $this->realDB = clone($DB); $DB = new mockDB(); } function tearDown() { global $DB; $DB = $this->realDB; } function testMoveSection() { global $DB; $course = new stdClass(); $course->id = 1; $sections = array(); for ($i = 1; $i < 11; $i++) { $sections[$i] = new stdClass(); $sections[$i]->id = $i; $sections[$i]->section = $i - 1; } $DB->expectOnce('get_records', array('course_sections', array('course' => $course->id))); $DB->setReturnValue('get_records', $sections); $this->assertFalse(move_section($course, 2, 3)); } }