<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>
        T&eacute;l&eacute;charger le framework de tests Simple Test - Tests unitaire et objets fantaisie pour PHP
    </title>
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
</head>
<body>
<div class="menu_back">
<div class="menu">
<h2>
<span class="chosen">SimpleTest</span>
</h2>
<ul>
<li>
<a href="overview.html">Overview</a>
</li>
<li>
<a href="unit_test_documentation.html">Unit tester</a>
</li>
<li>
<a href="group_test_documentation.html">Group tests</a>
</li>
<li>
<a href="server_stubs_documentation.html">Server stubs</a>
</li>
<li>
<a href="mock_objects_documentation.html">Mock objects</a>
</li>
<li>
<a href="partial_mocks_documentation.html">Partial mocks</a>
</li>
<li>
<a href="reporter_documentation.html">Reporting</a>
</li>
<li>
<a href="expectation_documentation.html">Expectations</a>
</li>
<li>
<a href="web_tester_documentation.html">Web tester</a>
</li>
<li>
<a href="form_testing_documentation.html">Testing forms</a>
</li>
<li>
<a href="authentication_documentation.html">Authentication</a>
</li>
<li>
<a href="browser_documentation.html">Scriptable browser</a>
</li>
</ul>
</div>
</div>
<h1>Simple Test pour PHP</h1>
<div class="content">
        
            <p>
                Le pr&eacute;sent article pr&eacute;suppose que vous soyez familier avec le concept de tests unitaires ainsi que celui de d&eacute;veloppement web avec le langage PHP. Il s'agit d'un guide pour le nouvel et impatient utilisateur de <a href="https://sourceforge.net/project/showfiles.php?group_id=76550">SimpleTest</a>. Pour une documentation plus compl&egrave;te, particuli&egrave;rement si vous d&eacute;couvrez les tests unitaires, consultez la <a href="http://www.lastcraft.com/unit_test_documentation.php">documentation en cours</a>, et pour des exemples de sc&eacute;narios de test, consultez le <a href="http://www.lastcraft.com/first_test_tutorial.php">tutorial sur les tests unitaires</a>.
            </p>
        
        <p>
<a class="target" name="unit">
<h2>Utiliser le testeur rapidement</h2>
</a>
</p>
            <p>
                Parmi les outils de test pour logiciel, le testeur unitaire est le plus proche du d&eacute;veloppeur. Dans un contexte de d&eacute;veloppement agile, le code de test se place juste &agrave; c&ocirc;t&eacute; du code source &eacute;tant donn&eacute; que tous les deux sont &eacute;crits simultan&eacute;ment. Dans ce contexte, SimpleTest aspire &agrave; &ecirc;tre une solution compl&egrave;te de test pour un d&eacute;veloppeur PHP et s'appelle "Simple" parce qu'elle devrait &ecirc;tre simple &agrave; utiliser et &agrave; &eacute;tendre. Ce nom n'&eacute;tait pas vraiment un bon choix. Non seulement cette solution inclut toutes les fonctions classiques qu'on est en droit d'attendre de la part des portages de <a href="http://www.junit.org/">JUnit</a> et des <a href="http://sourceforge.net/projects/phpunit/">PHPUnit</a>, mais elle inclut aussi les <a href="http://www.mockobjects.com/">objets fantaisie ou "mock objects"</a>. Sans compter quelques fonctionnalit&eacute;s de <a href="http://sourceforge.net/projects/jwebunit/">JWebUnit</a> : parmi celles-ci la navigation sur des pages web, les tests sur les cookies et l'envoi de formulaire.
            </p>
            <p>
                La d&eacute;monstration la plus rapide : l'exemple
            </p>
            <p>
                Supposons que nous sommes en train de tester une simple classe de log dans un fichier : elle s'appelle <span class="new_code">Log</span> dans <em>classes/Log.php</em>. Commen&ccedil;ons par cr&eacute;er un script de test, appel&eacute; <em>tests/log_test.php</em>. Son contenu est le suivant...
<pre>
<strong>&lt;?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');
?&gt;</strong>
</pre>
                Ici le r&eacute;pertoire <em>simpletest</em> est soit dans le dossier courant, soit dans les dossiers pour fichiers inclus. Vous auriez &agrave; &eacute;diter ces arborescences suivant l'endroit o&ugrave; vous avez install&eacute; SimpleTest. Ensuite cr&eacute;ons un sc&eacute;nario de test...
<pre>
&lt;?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');
<strong>
class TestOfLogging extends UnitTestCase {
}</strong>
?&gt;
</pre>
                A pr&eacute;sent il y a 5 lignes de code d'&eacute;chafaudage et toujours pas de test. Cependant &agrave; partir de cet instant le retour sur investissement arrive tr&egrave;s rapidement. Supposons que la classe <span class="new_code">Log</span> prenne le nom du fichier &agrave; &eacute;crire dans le constructeur et que nous ayons un r&eacute;pertoire temporaire dans lequel placer ce fichier...
<pre>
&lt;?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
    <strong>
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this-&gt;assertFalse(file_exists('/temp/test.log'));
        $log-&gt;message('Should write this to a file');
        $this-&gt;assertTrue(file_exists('/temp/test.log'));
    }</strong>
}
?&gt;
</pre>
                Au lancement du sc&eacute;nario de test, toutes les m&eacute;thodes qui commencent avec la cha&icirc;ne <span class="new_code">test</span> sont identifi&eacute;es puis ex&eacute;cut&eacute;es. D'ordinaire nous avons bien plusieurs m&eacute;thodes de tests. Les assertions dans les m&eacute;thodes de test envoient des messages vers le framework de test qui affiche imm&eacute;diatement le r&eacute;sultat. Cette r&eacute;ponse imm&eacute;diate est importante, non seulement lors d'un crash caus&eacute; par le code, mais aussi de mani&egrave;re &agrave; rapprocher l'affichage de l'erreur au plus pr&egrave;s du sc&eacute;nario de test concern&eacute;.
            </p>
            <p>
                Pour voir ces r&eacute;sultats lan&ccedil;ons effectivement les tests. S'il s'agit de l'unique sc&eacute;nario de test &agrave; lancer, on peut y arriver avec...
<pre>
&lt;?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');
require_once('../classes/log.php');

class TestOfLogging extends UnitTestCase {
    
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this-&gt;assertFalse(file_exists('/temp/test.log'));
        $log-&gt;message('Should write this to a file');
        $this-&gt;assertTrue(file_exists('/temp/test.log'));
    }
}
<strong>
$test = &amp;new TestOfLogging();
$test-&gt;run(new HtmlReporter());</strong>
?&gt;
</pre>
            </p>
            <p>
                En cas &eacute;chec, l'affichage ressemble &agrave;...
                <div class="demo">
                    <h1>testoflogging</h1>
                    <span class="fail">Fail</span>: testcreatingnewfile-&gt;True assertion failed.<br>
                    <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete.
                    <strong>1</strong> passes and <strong>1</strong> fails.</div>
                </div>
                ...et si &ccedil;a passe, on obtient...
                <div class="demo">
                    <h1>testoflogging</h1>
                    <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
                    <strong>2</strong> passes and <strong>0</strong> fails.</div>
                </div>
				Et si vous obtenez &ccedil;a...
                <div class="demo">
                    <b>Fatal error</b>:  Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b>
                </div>
				c'est qu'il vous manque le fichier <em>classes/Log.php</em> qui pourrait ressembler &agrave; : 
<pre>
&lt;?php
class Log {
    
        function Log($file_path) {
        }
}
?&gt;;
</pre>
            </p>
        
        <p>
<a class="target" name="group">
<h2>Construire des groupes de tests</h2>
</a>
</p>
            <p>
                Il est peu probable que dans une v&eacute;ritable application on ait uniquement besoin de passer un seul sc&eacute;nario de test. Cela veut dire que nous avons besoin de grouper les sc&eacute;narios dans un script de test qui peut, si n&eacute;cessaire, lancer tous les tests de l'application.
            </p>
            <p>
                Notre premi&egrave;re &eacute;tape est de supprimer les includes et de d&eacute;faire notre hack pr&eacute;c&eacute;dent...
<pre>
&lt;?php<strong>
require_once('../classes/log.php');</strong>

class TestOfLogging extends UnitTestCase {
    
    function testCreatingNewFile() {
        @unlink('/temp/test.log');
        $log = new Log('/temp/test.log');
        $this-&gt;assertFalse(file_exists('/temp/test.log'));
        $log-&gt;message('Should write this to a file');
        $this-&gt;assertTrue(file_exists('/temp/test.log'));<strong>
    }
}
?&gt;</strong>
</pre>
                Ensuite nous cr&eacute;ons un nouveau fichier appel&eacute; <em>tests/all_tests.php</em>. On y insert le code suivant...
<pre>
<strong>&lt;?php
require_once('simpletest/unit_tester.php');
require_once('simpletest/reporter.php');

$test = &amp;new GroupTest('All tests');
$test-&gt;addTestFile('log_test.php');
$test-&gt;run(new HtmlReporter());
?&gt;</strong>
</pre>
                Cette m&eacute;thode <span class="new_code">GroupTest::addTestFile()</span> va inclure le fichier de sc&eacute;narios de test et lire parmi toutes les nouvelles classes cr&eacute;&eacute;es celles qui sont issues de <span class="new_code">TestCase</span>. Dans un premier temps, seuls les noms sont stock&eacute;s, de la sorte le lanceur de test peut instancier la classe au fur et &agrave; mesure qu'il ex&eacute;cute votre suite de tests.
            </p>
            <p>
                Pour que &ccedil;a puisse marcher proprement le fichier de suite de tests ne devrait pas inclure aveuglement d'autres extensions de sc&eacute;narios de test qui n'ex&eacute;cuteraient pas effectivement de test. Le r&eacute;sultat pourrait &ecirc;tre que des tests suppl&eacute;mentaires soient alors &ecirc;tre comptabilis&eacute;s pendant l'ex&eacute;cution des tests. Ce n'est pas un probl&egrave;me grave mais pour &eacute;viter ce d&eacute;sagr&eacute;ment, il suffit d'ajouter la commande <span class="new_code">SimpleTestOptions::ignore()</span> quelque part dans le fichier de sc&eacute;nario de test. Par ailleurs le sc&eacute;nario de test ne devrait pas avoir &eacute;t&eacute; inclus ailleurs ou alors aucun sc&eacute;nario ne sera ajout&eacute; aux groupes de test. Il s'agirait l&agrave; d'une erreur autrement s&eacute;rieuse : si tous les classes de sc&eacute;nario de test sont charg&eacute;es par PHP, alors la m&eacute;thode <span class="new_code">GroupTest::addTestFile()</span> ne pourra pas les d&eacute;tecter.
            </p>
            <p>
                Pour afficher les r&eacute;sultats, il est seulement n&eacute;cessaire d'invoquer <em>tests/all_tests.php</em> &agrave; partir du serveur web.
            </p>
        
        <p>
<a class="target" name="mock">
<h2>Utiliser les objets fantaisie</h2>
</a>
</p>
            <p>
                Avan&ccedil;ons un peu plus dans le futur.
            </p>
            <p>
                Supposons que notre class logging soit test&eacute;e et termin&eacute;e. Supposons aussi que nous testons une autre classe qui ait besoin d'&eacute;crire des messages de log, disons <span class="new_code">SessionPool</span>. Nous voulons tester une m&eacute;thode qui ressemblera probablement &agrave; quelque chose comme...
<pre>
<strong>
class SessionPool {
    ...
    function logIn($username) {
        ...
        $this-&gt;_log-&gt;message('User $username logged in.');
        ...
    }
    ...
}
</strong>
</pre>
                Avec le concept de "r&eacute;utilisation de code" comme fil conducteur, nous utilisons notre class <span class="new_code">Log</span>. Un sc&eacute;nario de test classique ressemblera peut-&ecirc;tre &agrave;...
<pre>
<strong>
&lt;?php
require_once('../classes/log.php');
require_once('../classes/session_pool.php');

class TestOfSessionLogging extends UnitTestCase {
    
    function setUp() {
        @unlink('/temp/test.log');
    }
    
    function tearDown() {
        @unlink('/temp/test.log');
    }
    
    function testLogInIsLogged() {
        $log = new Log('/temp/test.log');
        $session_pool = &amp;new SessionPool($log);
        $session_pool-&gt;logIn('fred');
        $messages = file('/temp/test.log');
        $this-&gt;assertEqual($messages[0], "User fred logged in.\n");
    }
}
?&gt;</strong>
</pre>
                Le design de ce sc&eacute;nario de test n'est pas compl&egrave;tement mauvais, mais on peut l'am&eacute;liorer. Nous passons du temps &agrave; tripoter les fichiers de log qui ne font pas partie de notre test. Pire, nous avons cr&eacute;&eacute; des liens de proximit&eacute; entre la classe <span class="new_code">Log</span> et ce test. Que se passerait-il si nous n'utilisions plus de fichiers, mais la biblioth&egrave;que <em>syslog</em> &agrave; la place ? Avez-vous remarqu&eacute; le retour chariot suppl&eacute;mentaire &agrave; la fin du message ? A-t-il &eacute;t&eacute; ajout&eacute; par le loggueur ? Et si il ajoutait aussi un timestamp ou d'autres donn&eacute;es ?
            </p>
            <p>
                L'unique partie &agrave; tester r&eacute;ellement est l'envoi d'un message pr&eacute;cis au loggueur. Nous r&eacute;duisons le couplage en cr&eacute;ant une fausse classe de logging : elle ne fait qu'enregistrer le message pour le test, mais ne produit aucun r&eacute;sultat. Sauf qu'elle doit ressembler exactement &agrave; l'original.
            </p>
            <p>
                Si l'objet fantaisie n'&eacute;crit pas dans un fichier alors nous nous &eacute;pargnons la suppression du fichier avant et apr&egrave;s le test. Nous pourrions m&ecirc;me nous &eacute;pargner quelques lignes de code suppl&eacute;mentaires si l'objet fantaisie pouvait ex&eacute;cuter l'assertion.
            <p>
            </p>
                Trop beau pour &ecirc;tre vrai ? Par chance on peut cr&eacute;er un tel objet tr&egrave;s facilement...
<pre>
&lt;?php
require_once('../classes/log.php');
require_once('../classes/session_pool.php');<strong>
Mock::generate('Log');</strong>

class TestOfSessionLogging extends UnitTestCase {
    
    function testLogInIsLogged() {<strong>
        $log = &amp;new MockLog($this);
        $log-&gt;expectOnce('message', array('User fred logged in.'));</strong>
        $session_pool = &amp;new SessionPool($log);
        $session_pool-&gt;logIn('fred');<strong>
        $log-&gt;tally();</strong>
    }
}
?&gt;
</pre>
                L'appel <span class="new_code">tally()</span> est n&eacute;cessaire pour annoncer &agrave; l'objet fantaisie qu'il n'y aura plus d'appels ult&eacute;rieurs. Sans &ccedil;a l'objet fantaisie pourrait attendre pendant une &eacute;ternit&eacute; l'appel de la m&eacute;thode sans jamais pr&eacute;venir le sc&eacute;nario de test. Les autres tests sont d&eacute;clench&eacute;s automatiquement quand l'appel &agrave; <span class="new_code">message()</span> est invoqu&eacute; sur l'objet <span class="new_code">MockLog</span>. L'appel <span class="new_code">mock</span> va d&eacute;clencher une comparaison des param&egrave;tres et ensuite envoyer le message "pass" ou "fail" au test pour l'affichage. Des jokers peuvent &ecirc;tre inclus ici aussi afin d'emp&ecirc;cher que les tests ne deviennent trop sp&eacute;cifiques.
            </p>
            <p>
                Les objets fantaisie dans la suite SimpleTest peuvent avoir un ensemble de valeurs de sortie arbitraires, des s&eacute;quences de sorties, des valeurs de sortie s&eacute;lectionn&eacute;es &agrave; partir des arguments d'entr&eacute;e, des s&eacute;quences de param&egrave;tres attendus et des limites sur le nombre de fois qu'une m&eacute;thode peut &ecirc;tre invoqu&eacute;e.
            </p>
            <p>
                Pour que ce test fonctionne la librairie avec les objets fantaisie doit &ecirc;tre incluse dans la suite de tests, par exemple dans <em>all_tests.php</em>.
            </p>
        
        <p>
<a class="target" name="web">
<h2>Tester une page web</h2>
</a>
</p>
            <p>
                Une des exigences des sites web, c'est qu'ils produisent des pages web. Si vous construisez un projet de A &agrave; Z et que vous voulez int&eacute;grer des tests au fur et &agrave; mesure alors vous voulez un outil qui puisse effectuer une navigation automatique et en examiner le r&eacute;sultat. C'est le boulot d'un testeur web.
            </p>
            <p>
                Effectuer un test web via SimpleTest reste assez primitif : il n'y a pas de javascript par exemple. Pour vous donner une id&eacute;e, voici un exemple assez trivial : aller chercher une page web, &agrave; partir de l&agrave; naviguer vers la page "about" et finalement tester un contenu d&eacute;termin&eacute; par le client.
<pre>
&lt;?php<strong>
require_once('simpletest/web_tester.php');</strong>
require_once('simpletest/reporter.php');
<strong>
class TestOfAbout extends WebTestCase {
    
    function setUp() {
        $this-&gt;get('http://test-server/index.php');
        $this-&gt;clickLink('About');
    }
    
    function testSearchEngineOptimisations() {
        $this-&gt;assertTitle('A long title about us for search engines');
        $this-&gt;assertWantedPattern('/a popular keyphrase/i');
    }
}</strong>
$test = &amp;new TestOfAbout();
$test-&gt;run(new HtmlReporter());
?&gt;
</pre>
                Avec ce code comme test de recette, vous pouvez vous assurer que le contenu corresponde toujours aux sp&eacute;cifications &agrave; la fois des d&eacute;veloppeurs et des autres parties prenantes au projet.
            </p>
            <p>
                <a href="http://sourceforge.net/projects/simpletest/"><img src="http://sourceforge.net/sflogo.php?group_id=76550&amp;type=5" width="210" height="62" border="0" alt="SourceForge.net Logo"></a>
            </p>
        
    </div>
<div class="copyright">
            Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
        </div>
</body>
</html>