Documentation sur le rapporteur de test

This page...

SimpleTest suit plutôt plus que moins le modèle MVC (Modèle-Vue-Contrôleur). Les classes "reporter" sont les vues et les modèles sont vos scénarios de test et leur hiérarchie. Le contrôleur est le plus souvent masqué à l'utilisateur de SimpleTest à moins de vouloir changer la façon dont les tests sont effectivement exécutés, auquel cas il est possible de surcharger les objets "runner" (ceux de l'exécuteur) depuis l'intérieur d'un scénario de test. Comme d'habitude avec MVC, le contrôleur est plutôt indéfini et il existe d'autres endroits pour contrôler l'exécution des tests.

Les résultats rapportés au format HTML

L'affichage par défaut est minimal à l'extrême. Il renvoie le succès ou l'échec avec les barres conventionnelles - rouge et verte - et affichent une trace d'arborescence des groupes de test pour chaque assertion erronée. Voici un tel échec...

File test

Fail: createnewfile->True assertion failed.
1/1 test cases complete. 0 passes, 1 fails and 0 exceptions.
Alors qu'ici tous les tests passent...

File test

1/1 test cases complete. 1 passes, 0 fails and 0 exceptions.
La bonne nouvelle, c'est qu'il existe pas mal de points dans la hiérarchie de l'affichage pour créer des sous-classes.

Pour l'affichage basé sur des pages web, il y a la classe HtmlReporter avec la signature suivante...

class HtmlReporter extends SimpleReporter {
    public __construct($encoding) { ... }
    public makeDry(boolean $is_dry) { ... }
    public void paintHeader(string $test_name) { ... }
    public void sendNoCacheHeaders() { ... }
    public void paintFooter(string $test_name) { ... }
    public void paintGroupStart(string $test_name, integer $size) { ... }
    public void paintGroupEnd(string $test_name) { ... }
    public void paintCaseStart(string $test_name) { ... }
    public void paintCaseEnd(string $test_name) { ... }
    public void paintMethodStart(string $test_name) { ... }
    public void paintMethodEnd(string $test_name) { ... }
    public void paintFail(string $message) { ... }
    public void paintPass(string $message) { ... }
    public void paintError(string $message) { ... }
    public void paintException(string $message) { ... }
    public void paintMessage(string $message) { ... }
    public void paintFormattedMessage(string $message) { ... }
    protected string getCss() { ... }
    public array getTestList() { ... }
    public integer getPassCount() { ... }
    public integer getFailCount() { ... }
    public integer getExceptionCount() { ... }
    public integer getTestCaseCount() { ... }
    public integer getTestCaseProgress() { ... }
}
Voici ce que certaines de ces méthodes veulent dire. Premièrement les méthodes d'affichage que vous voudrez probablement surcharger... Il y a aussi des accesseurs pour aller chercher l'information sur l'état courant de la suite de test. Vous les utiliserez pour enrichir l'affichage... Une modification simple : demander à l'HtmlReporter d'afficher aussi bien les succès que les échecs et les erreurs...
class ReporterShowingPasses extends HtmlReporter {
    
    function paintPass($message) {
        parent::paintPass($message);
        print "<span class=\"pass\">Pass</span>: ";
        $breadcrumb = $this->getTestList();
        array_shift($breadcrumb);
        print implode("-&gt;", $breadcrumb);
        print "-&gt;$message<br />\n";
    }
    
    protected function getCss() {
        return parent::getCss() . ' .pass { color: green; }';
    }
}

Une méthode qui a beaucoup fait jaser reste la méthode makeDry(). Si vous lancez cette méthode, sans paramètre, sur le rapporteur avant que la suite de test ne soit exécutée alors aucune méthode de test ne sera appelée. Vous continuerez à avoir les évènements entrants et sortants des méthodes et scénarios de test, mais aucun succès ni échec ou erreur, parce que le code de test ne sera pas exécuté.

La raison ? Pour permettre un affichage complexe d'une IHM (ou GUI) qui permettrait la sélection de scénarios de test individuels. Afin de construire une liste de tests possibles, ils ont besoin d'un rapport sur la structure du test pour l'affichage, par exemple, d'une vue en arbre de la suite de test. Avec un rapporteur lancé sur une exécution sèche qui ne renverrait que les évènements d'affichage, cela devient facilement réalisable.

Etendre le rapporteur

Plutôt que de modifier l'affichage existant, vous voudrez peut-être produire une présentation HTML complètement différente, ou même générer une version texte ou XML. Plutôt que de surcharger chaque méthode dans HtmlReporter nous pouvons nous rendre une étape plus haut dans la hiérarchie de classe vers SimpleReporter dans le fichier source simple_test.php.

Un affichage sans rien, un canevas vierge pour votre propre création, serait...

require_once('simpletest/simpletest.php');

class MyDisplay extends SimpleReporter {
    
    function paintHeader($test_name) { }
    
    function paintFooter($test_name) { }
    
    function paintStart($test_name, $size) {
        parent::paintStart($test_name, $size);
    }
    
    function paintEnd($test_name, $size) {
        parent::paintEnd($test_name, $size);
    }
    
    function paintPass($message) {
        parent::paintPass($message);
    }
    
    function paintFail($message) {
        parent::paintFail($message);
    }
    
    function paintError($message) {
        parent::paintError($message);
    }
    
    function paintException($exception) {
        parent::paintException($exception);
    }
}
Aucune sortie ne viendrait de cette classe jusqu'à un ajout de votre part.

Sauf qu'il y a un problème : en utilisant cette cette classe de bas niveau, vous devez explicitement l'invoquer dans les scripts de test. La commande "autorun" ne sera pas capable d'utiliser son contexte courant (qu'elle soit lancée dans un navigateur web ou via une ligne de commande) pour sélectionner le rapporteur.

Vous invoquez explicitement la lanceur de tests comme suit...

<?php
require_once('simpletest/autorun.php');

$test = new TestSuite('File test');
$test->addFile('tests/file_test.php');
$test->run(new MyReporter());
?>
...ou peut-être comme cela...
<?php
require_once('simpletest/simpletest.php');
require_once('my_reporter.php');

class MyTest extends TestSuite {
    function __construct() {
        parent::__construct();
        $this->addFile('tests/file_test.php');
    }
}

$test = new MyTest();
$test->run(new MyReporter());
?>
Nous verrons plus comment l'intégrer avec l'"autorun".

Le rapporteur en ligne de commande

SimpleTest est aussi livré avec un rapporteur en ligne de commande, minime lui aussi. Pour utiliser le rapporteur en ligne de commande explicitement, il suffit de l'intervertir avec celui de la version HTML...

<?php
require_once('simpletest/autorun.php');

$test = new TestSuite('File test');
$test->addFile('tests/file_test.php');
$test->run(new TextReporter());
?>
Et ensuite d'invoquer la suite de test à partir d'une ligne de commande...
php file_test.php
Bien sûr vous aurez besoin d'installer PHP en ligne de commande. Une suite de test qui passerait toutes ses assertions ressemble à...
File test
OK
Test cases run: 1/1, Passes: 1, Failures: 0, Exceptions: 0
Un échec déclenche un affichage comme...
File test
1) True assertion failed.
    in createNewFile
FAILURES!!!
Test cases run: 1/1, Passes: 0, Failures: 1, Exceptions: 0

Une des principales raisons pour utiliser une suite de test en ligne de commande tient dans l'utilisation possible du testeur avec un processus automatisé. Pour fonctionner comme il faut dans des scripts shell le script de test devrait renvoyer un code de sortie non-nul suite à un échec. Si une suite de test échoue la valeur false est renvoyée par la méthode SimpleTest::run(). Nous pouvons utiliser ce résultat pour terminer le script avec la bonne valeur renvoyée...

<?php
require_once('simpletest/autorun.php');

$test = new TestSuite('File test');
$test->addFile('tests/file_test.php');
exit ($test->run(new TextReporter()) ? 0 : 1);
?>
Bien sûr l'objectif ne serait pas de créer deux scripts de test, l'un en ligne de commande et l'autre pour un navigateur web, pour chaque suite de test. Le rapporteur en ligne de commande inclut une méthode pour déterminer l'environnement d'exécution...
<?php
require_once('simpletest/autorun.php');

$test = new TestSuite('File test');
$test->addFile('tests/file_test.php');
if (TextReporter::inCli()) {
    exit ($test->run(new TextReporter()) ? 0 : 1);
}
$test->run(new HtmlReporter());
?>
Il s'agit là de la forme utilisée par SimpleTest lui-même. Quand vous utilisez l'"autorun.php" et qu'aucun test n'a été lancé avant la fin, c'est quasiment le code que SimpleTest lancera pour vous implicitement.

En d'autres termes, ceci donne le même résultat...

<?php
require_once('simpletest/autorun.php');

class MyTest extends TestSuite {
    function __construct() {
        parent::__construct();
        $this->addFile('tests/file_test.php');
    }
}
?>

Test distant

SimpleTest est livré avec une classe XmlReporter utilisée pour de la communication interne. Lors de son exécution, le résultat ressemble à...

<?xml version="1.0"?>
<run>
  <group size="4">
    <name>Remote tests</name>
    <group size="4">
      <name>Visual test with 48 passes, 48 fails and 4 exceptions</name>
      <case>
        <name>testofunittestcaseoutput</name>
        <test>
          <name>testofresults</name>
          <pass>This assertion passed</pass>
          <fail>This assertion failed</fail>
        </test>
        <test>
          ...
        </test>
      </case>
    </group>
  </group>
</run>
Pour faire en sorte ue vos scénarios de test produisent ce format, dans la ligne de commande, ajoutez le flag --xml.
php my_test.php --xml
Vous pouvez faire la même chose dans le navigation web en ajoutant le paramètre xml=1 dans l'URL. N'importe quelle valeur "true" fera l'affaire.

Vous pouvez utiliser ce format avec le parseur fourni dans SimpleTest lui-même. Il s'agit de SimpleTestXmlParser et se trouve xml.php à l'intérieur du paquet SimpleTest...

<?php
require_once('simpletest/xml.php');
    
...
$parser = new SimpleTestXmlParser(new HtmlReporter());
$parser->parse($test_output);
?>
$test_output devrait être au format XML, à partir du rapporteur XML, et pourrait venir d'une exécution en ligne de commande d'un scénario de test. Le parseur envoie des évènements au rapporteur exactement comme tout autre exécution de test. Il y a des occasions bizarres dans lesquelles c'est en fait très utile.

Le plus courant, c'est quand vous voulez isoler un test sensible au crash. Vous pouvez collecter la sortie XML en utilisant l'opérateur antiquote (Ndt : backtick) à partir d'un autre test. De la sorte, il tourne dans son propre processus...

<?php
require_once('simpletest/xml.php');

if (TextReporter::inCli()) {
    $parser = new SimpleTestXmlParser(new TextReporter());
} else {
    $parser = new SimpleTestXmlParser(new HtmlReporter());
}
$parser->parse(`php flakey_test.php --xml`);
?>

Un autre cas est celui des très longues suites de tests.

Elles peuvent venir à bout de la limite de mémoire par défaut d'un process PHP - 16Mb. En plaçant la sortie des groupes de test dans du XML et leur exécution dans des process différents, le résultat peut être parsé à nouveau pour agréger les résultats avec moins d'impact sur le test au premier niveau.

Parce que la sortie XML peut venir de n'importe où, ça ouvre des possibilités d'agrégation d'exécutions de test depuis des serveur distants. Un scénario de test pour le réaliser existe déjà à l'intérieur du framework SimpleTest, mais il est encore expérimental...

<?php
require_once('../remote.php');
require_once('simpletest/autorun.php');
    
$test_url = ...;
$dry_url = ...;

class MyTestOnAnotherServer extends RemoteTestCase {
    function __construct() {
        $test_url = ...
        parent::__construct($test_url, $test_url . ' --dry');
    }
}
?>
RemoteTestCase prend la localisation réelle du lanceur de test, tout simplement un page web au format XML. Il prend aussi l'URL d'un rapporteur initié pour effectuer une exécution sèche. Cette technique est employée pour que les progrès soient correctement rapportés vers le haut. RemoteTestCase peut être ajouté à une suite de test comme n'importe quelle autre suite de tests.

References and related information...