delete, select & insert upgraded More utility function (node to array, arrayToNode) XMLDB special move command PHP Unit Test
		
			
				
	
	
		
			779 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			779 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<html>
 | 
						|
<head>
 | 
						|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 | 
						|
<title>Documentation SimpleTest : les objets fantaise</title>
 | 
						|
<link rel="stylesheet" type="text/css" href="docs.css" title="Styles">
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
<div class="menu_back"><div class="menu">
 | 
						|
<a href="index.html">SimpleTest</a>
 | 
						|
                |
 | 
						|
                <a href="overview.html">Overview</a>
 | 
						|
                |
 | 
						|
                <a href="unit_test_documentation.html">Unit tester</a>
 | 
						|
                |
 | 
						|
                <a href="group_test_documentation.html">Group tests</a>
 | 
						|
                |
 | 
						|
                <a href="mock_objects_documentation.html">Mock objects</a>
 | 
						|
                |
 | 
						|
                <a href="partial_mocks_documentation.html">Partial mocks</a>
 | 
						|
                |
 | 
						|
                <a href="reporter_documentation.html">Reporting</a>
 | 
						|
                |
 | 
						|
                <a href="expectation_documentation.html">Expectations</a>
 | 
						|
                |
 | 
						|
                <a href="web_tester_documentation.html">Web tester</a>
 | 
						|
                |
 | 
						|
                <a href="form_testing_documentation.html">Testing forms</a>
 | 
						|
                |
 | 
						|
                <a href="authentication_documentation.html">Authentication</a>
 | 
						|
                |
 | 
						|
                <a href="browser_documentation.html">Scriptable browser</a>
 | 
						|
</div></div>
 | 
						|
<h1>Documentation sur les objets fantaisie</h1>
 | 
						|
        This page...
 | 
						|
        <ul>
 | 
						|
<li>
 | 
						|
            <a href="#quoi">Que sont les objets fantaisie ?</a>
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            <a href="#creation">Créer des objets fantaisie</a>.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            <a href="#bouchon">L'objet fantaisie - acteur</a> ou bouchon.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            <a href="#attentes">L'objet fantaisie - critique</a> avec des attentes.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            <a href="#approches">D'autres approches</a>
 | 
						|
            y compris des librairies d'objets fantaisie.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            Utiliser les objets fantaisie avec
 | 
						|
            <a href="#autres_testeurs">d'autres testeurs unitaires</a>.
 | 
						|
        </li>
 | 
						|
</ul>
 | 
						|
<div class="content">
 | 
						|
        <p><a class="target" name="quoi"><h2>Que sont les objets fantaisie ?</h2></a></p>
 | 
						|
            <p>
 | 
						|
                Les objets fantaisie - ou "mock objects" en anglais -
 | 
						|
                ont deux rôles pendant un scénario de test : acteur et critique.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Le comportement d'acteur est celui de simuler
 | 
						|
                des objets difficiles à initialiser ou trop consommateurs
 | 
						|
                en temps pendant un test.
 | 
						|
                Le cas classique est celui de la connexion à une base de données.
 | 
						|
                Mettre sur pied une base de données de test au lancement
 | 
						|
                de chaque test ralentirait considérablement les tests
 | 
						|
                et en plus exigerait l'installation d'un moteur
 | 
						|
                de base de données ainsi que des données sur la machine de test.
 | 
						|
                Si nous pouvons simuler la connexion
 | 
						|
                et renvoyer des données à notre guise
 | 
						|
                alors non seulement nous gagnons en pragmatisme
 | 
						|
                sur les tests mais en sus nous pouvons nourrir
 | 
						|
                notre base avec des données falsifiées
 | 
						|
                et voir comment il répond. Nous pouvons
 | 
						|
                simuler une base de données en suspens ou
 | 
						|
                d'autres cas extrêmes sans avoir à créer
 | 
						|
                une véritable panne de base de données.
 | 
						|
                En d'autres termes nous pouvons gagner
 | 
						|
                en contrôle sur l'environnement de test.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Si les objets fantaisie ne se comportaient que comme
 | 
						|
                des acteurs alors on les connaîtrait sous le nom de
 | 
						|
                <a href="server_stubs_documentation.html">bouchons serveur</a>.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Cependant non seulement les objets fantaisie jouent
 | 
						|
                un rôle (en fournissant à la demande les valeurs requises)
 | 
						|
                mais en plus ils sont aussi sensibles aux messages qui
 | 
						|
                leur sont envoyés (par le biais d'attentes).
 | 
						|
                En posant les paramètres attendus d'une méthode
 | 
						|
                ils agissent comme des gardiens :
 | 
						|
                un appel sur eux doit être réalisé correctement.
 | 
						|
                Si les attentes ne sont pas atteintes ils nous épargnent
 | 
						|
                l'effort de l'écriture d'une assertion de test avec
 | 
						|
                échec en réalisant cette tâche à notre place.
 | 
						|
                Dans le cas d'une connexion à une base de données
 | 
						|
                imaginaire ils peuvent tester si la requête, disons SQL,
 | 
						|
                a bien été formé par l'objet qui utilise cette connexion.
 | 
						|
                Mettez-les sur pied avec des attentes assez précises
 | 
						|
                et vous verrez que vous n'aurez presque plus d'assertion à écrire manuellement.
 | 
						|
            </p>
 | 
						|
        
 | 
						|
        <p><a class="target" name="creation"><h2>Créer des objets fantaisie</h2></a></p>
 | 
						|
            <p>
 | 
						|
                Comme pour la création des bouchons serveur, tout ce dont
 | 
						|
                nous avons besoin c'est d'un classe existante.
 | 
						|
                La fameuse connexion à une base de données qui ressemblerait à...
 | 
						|
<pre>
 | 
						|
<strong>class DatabaseConnection {
 | 
						|
    function DatabaseConnection() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function query() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function selectQuery() {
 | 
						|
    }
 | 
						|
}</strong>
 | 
						|
</pre>
 | 
						|
                Cette classe n'a pas encore besoin d'être implémentée.
 | 
						|
                Pour en créer sa version fantaisie nous devons juste
 | 
						|
                inclure la librairie d'objet fantaisie puis lancer le générateur...
 | 
						|
<pre>
 | 
						|
<strong>require_once('simpletest/unit_tester.php');
 | 
						|
require_once('simpletest/mock_objects.php');
 | 
						|
require_once('database_connection.php');
 | 
						|
 | 
						|
Mock::generate('DatabaseConnection');</strong>
 | 
						|
</pre>
 | 
						|
                Ceci génère une classe clone appelée <span class="new_code">MockDatabaseConnection</span>.
 | 
						|
                Nous pouvons désormais créer des instances de
 | 
						|
                cette nouvelle classe à l'intérieur même de notre scénario de test...
 | 
						|
<pre>
 | 
						|
require_once('simpletest/unit_tester.php');
 | 
						|
require_once('simpletest/mock_objects.php');
 | 
						|
require_once('database_connection.php');
 | 
						|
 | 
						|
Mock::generate('DatabaseConnection');
 | 
						|
<strong>
 | 
						|
class MyTestCase extends UnitTestCase {
 | 
						|
    
 | 
						|
    function testSomething() {
 | 
						|
        $connection = &new MockDatabaseConnection($this);
 | 
						|
    }
 | 
						|
}</strong>
 | 
						|
</pre>
 | 
						|
                Contrairement aux bouchons, le constructeur
 | 
						|
                d'une classe fantaisie a besoin d'une référence au scénario
 | 
						|
                de test pour pouvoir transmettre les succès
 | 
						|
                et les échecs pendant qu'il vérifie les attentes.
 | 
						|
                Concrètement ça veut dire que les objets fantaisie
 | 
						|
                ne peuvent être utilisés qu'au sein d'un scénario de test.
 | 
						|
                Malgré tout, cette puissance supplémentaire implique
 | 
						|
                que les bouchons ne sont que rarement utilisés
 | 
						|
                si des objets fantaisie sont disponibles.
 | 
						|
            </p>
 | 
						|
        
 | 
						|
        <p><a class="target" name="bouchon"><h2>Objets fantaisie en action</h2></a></p>
 | 
						|
            <p>
 | 
						|
                La version fantaisie d'une classe contient
 | 
						|
                toutes les méthodes de l'originale.
 | 
						|
                De la sorte une opération comme
 | 
						|
                <span class="new_code">$connection->query()</span>
 | 
						|
                est encore possible.
 | 
						|
                Tout comme avec les bouchons, nous pouvons remplacer
 | 
						|
                la valeur nulle renvoyée par défaut...
 | 
						|
<pre>
 | 
						|
<strong>$connection->setReturnValue('query', 37);</strong>
 | 
						|
</pre>
 | 
						|
                Désormais à chaque appel de
 | 
						|
                <span class="new_code">$connection->query()</span>
 | 
						|
                nous recevons comme résultat 37.
 | 
						|
                Tout comme avec les bouchons nous pouvons utiliser
 | 
						|
                des jokers et surcharger le paramètre joker.
 | 
						|
                Nous pouvons aussi ajouter des méthodes supplémentaires
 | 
						|
                à l'objet fantaisie lors de sa génération
 | 
						|
                et lui choisir un nom de classe qui lui soit propre...
 | 
						|
<pre>
 | 
						|
<strong>Mock::generate('DatabaseConnection', 'MyMockDatabaseConnection', array('setOptions'));</strong>
 | 
						|
</pre>
 | 
						|
                Ici l'objet fantaisie se comportera comme
 | 
						|
                si <span class="new_code">setOptions()</span> existait dans la classe originale.
 | 
						|
                C'est pratique si une classe a utilisé le mécanisme
 | 
						|
                <span class="new_code">overload()</span> de PHP pour ajouter des méthodes dynamiques.
 | 
						|
                Vous pouvez créer des fantaisies spéciales pour simuler cette situation.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Tous les modèles disponibles avec les bouchons serveur
 | 
						|
                le sont également avec les objets fantaisie...
 | 
						|
<pre>
 | 
						|
class Iterator {
 | 
						|
    function Iterator() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function next() {
 | 
						|
    }
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                Une nouvelle fois, supposons que cet itérateur
 | 
						|
                ne retourne que du texte jusqu'au moment où il atteint
 | 
						|
                son terme, quand il renvoie <span class="new_code">false</span>.
 | 
						|
                Nous pouvons le simuler avec...
 | 
						|
<pre>
 | 
						|
Mock::generate('Iterator');
 | 
						|
 | 
						|
class IteratorTest extends UnitTestCase() {
 | 
						|
    
 | 
						|
    function testASequence() {<strong>
 | 
						|
        $iterator = &new MockIterator($this);
 | 
						|
        $iterator->setReturnValue('next', false);
 | 
						|
        $iterator->setReturnValueAt(0, 'next', 'First string');
 | 
						|
        $iterator->setReturnValueAt(1, 'next', 'Second string');</strong>
 | 
						|
        ...
 | 
						|
    }
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                Au moment du premier appel à <span class="new_code">next()</span>
 | 
						|
                sur l'itérateur fantaisie il renverra tout d'abord
 | 
						|
                "First string", puis ce sera au tour de
 | 
						|
                "Second string" au deuxième appel
 | 
						|
                et ensuite pour tout appel suivant <span class="new_code">false</span>
 | 
						|
                sera renvoyé.
 | 
						|
                Ces valeurs renvoyées successivement sont prioritaires
 | 
						|
                sur la valeur constante retournée.
 | 
						|
                Cette dernière est un genre de valeur par défaut si vous voulez.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Reprenons aussi le conteneur d'information bouchonné
 | 
						|
                avec des pairs clef / valeur...
 | 
						|
<pre>
 | 
						|
class Configuration {
 | 
						|
    function Configuration() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function getValue($key) {
 | 
						|
    }
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                Il s'agit là d'une situation classique
 | 
						|
                d'utilisation d'objets fantaisie étant donné
 | 
						|
                que la configuration peut varier grandement de machine à machine :
 | 
						|
                ça contraint fortement la fiabilité de nos tests
 | 
						|
                si nous l'utilisons directement.
 | 
						|
                Le problème est que toutes les données nous parviennent
 | 
						|
                à travers la méthode <span class="new_code">getValue()</span>
 | 
						|
                et que nous voulons des résultats différents pour des clefs différentes.
 | 
						|
                Heureusement les objets fantaisie ont un système de filtrage...
 | 
						|
<pre>
 | 
						|
<strong>$config = &new MockConfiguration($this);
 | 
						|
$config->setReturnValue('getValue', 'primary', array('db_host'));
 | 
						|
$config->setReturnValue('getValue', 'admin', array('db_user'));
 | 
						|
$config->setReturnValue('getValue', 'secret', array('db_password'));</strong>
 | 
						|
</pre>
 | 
						|
                Le paramètre en plus est une liste d'arguments
 | 
						|
                à faire correspondre. Dans ce cas nous essayons
 | 
						|
                de faire correspondre un unique argument :
 | 
						|
                en l'occurrence la clef recherchée.
 | 
						|
                Maintenant que la méthode <span class="new_code">getValue()</span>
 | 
						|
                est invoquée sur l'objet fantaisie...
 | 
						|
<pre>
 | 
						|
$config->getValue('db_user')
 | 
						|
</pre>
 | 
						|
                ...elle renverra "admin".
 | 
						|
                Elle le trouve en essayant de faire correspondre
 | 
						|
                les arguments entrants dans sa liste
 | 
						|
                d'arguments sortants les uns après les autres
 | 
						|
                jusqu'au moment où une correspondance exacte est atteinte.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Il y a des fois où vous souhaitez
 | 
						|
                qu'un objet spécifique soit servi par la fantaisie
 | 
						|
                plutôt qu'une copie.
 | 
						|
                De nouveau c'est identique au mécanisme des bouchons serveur...
 | 
						|
<pre>
 | 
						|
class Thing {
 | 
						|
}
 | 
						|
 | 
						|
class Vector {
 | 
						|
    function Vector() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function get($index) {
 | 
						|
    }
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                Dans ce cas vous pouvez placer une référence
 | 
						|
                dans la liste renvoyée par l'objet fantaisie...
 | 
						|
<pre>
 | 
						|
$thing = new Thing();<strong>
 | 
						|
$vector = &new MockVector($this);
 | 
						|
$vector->setReturnReference('get', $thing, array(12));</strong>
 | 
						|
</pre>
 | 
						|
                Avec cet arrangement vous savez qu'à chaque appel
 | 
						|
                de <span class="new_code">$vector->get(12)</span>
 | 
						|
                le même <span class="new_code">$thing</span> sera renvoyé.
 | 
						|
            </p>
 | 
						|
        
 | 
						|
        <p><a class="target" name="attentes"><h2>Objets fantaisie en critique</h2></a></p>
 | 
						|
            <p>
 | 
						|
                Même si les bouchons serveur vous isolent
 | 
						|
                du désordre du monde réel, il ne s'agit là que
 | 
						|
                de la moitié du bénéfice potentiel.
 | 
						|
                Vous pouvez avoir une classe de test recevant
 | 
						|
                les messages ad hoc, mais est-ce que votre nouvelle classe
 | 
						|
                renvoie bien les bons ?
 | 
						|
                Le tester peut devenir cafouillis sans une librairie d'objets fantaisie.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Pour l'exemple, prenons une classe <span class="new_code">SessionPool</span>
 | 
						|
                à laquelle nous allons ajouter une fonction de log.
 | 
						|
                Plutôt que de complexifier la classe originale,
 | 
						|
                nous souhaitons ajouter ce comportement avec un décorateur (GOF).
 | 
						|
                Pour l'instant le code de <span class="new_code">SessionPool</span> ressemble à...
 | 
						|
<pre>
 | 
						|
<strong>class SessionPool {
 | 
						|
    function SessionPool() {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
    
 | 
						|
    function &findSession($cookie) {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
    ...
 | 
						|
}
 | 
						|
 | 
						|
class Session {
 | 
						|
    ...
 | 
						|
}</strong>
 | 
						|
 | 
						|
</pre>
 | 
						|
                Alors que pour notre code de log, nous avons...
 | 
						|
<pre><strong>
 | 
						|
class Log {
 | 
						|
    function Log() {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
    
 | 
						|
    function message() {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
class LoggingSessionPool {
 | 
						|
    function LoggingSessionPool(&$session_pool, &$log) {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
    
 | 
						|
    function &findSession($cookie) {
 | 
						|
        ...
 | 
						|
    }
 | 
						|
    ...
 | 
						|
}</strong>
 | 
						|
</pre>
 | 
						|
                Dans tout ceci, la seule classe à tester est
 | 
						|
                <span class="new_code">LoggingSessionPool</span>. En particulier,
 | 
						|
                nous voulons vérifier que la méthode <span class="new_code">findSession()</span>
 | 
						|
                est appelée avec le bon identifiant de session au sein du cookie
 | 
						|
                et qu'elle renvoie bien le message "Starting session $cookie"
 | 
						|
                au loggueur.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Bien que nous ne testions que quelques lignes
 | 
						|
                de code de production, voici la liste des choses
 | 
						|
                à faire dans un scénario de test conventionnel :
 | 
						|
                <ol>
 | 
						|
                    <li>Créer un objet de log.</li>
 | 
						|
                    <li>Indiquer le répertoire d'écriture du fichier de log.</li>
 | 
						|
                    <li>Modifier les droits sur le répertoire pour pouvoir y écrire le fichier.</li>
 | 
						|
                    <li>Créer un objet <span class="new_code">SessionPool</span>.</li>
 | 
						|
                    <li>Lancer une session, ce qui demande probablement pas mal de choses.</li>
 | 
						|
                    <li>Invoquer <span class="new_code">findSession()</span>.</li>
 | 
						|
                    <li>Lire le nouvel identifiant de session (en espérant qu'il existe un accesseur !).</li>
 | 
						|
                    <li>Lever une assertion de test pour vérifier que cet identifiant correspond bien au cookie.</li>
 | 
						|
                    <li>Lire la dernière ligne du fichier de log.</li>
 | 
						|
                    <li>Supprimer avec une (ou plusieurs) expression rationnelle les timestamps de log en trop, etc.</li>
 | 
						|
                    <li>Vérifier que le message de session est bien dans le texte.</li>
 | 
						|
                </ol>
 | 
						|
                Pas étonnant que les développeurs détestent
 | 
						|
                écrire des tests quand ils sont aussi ingrats.
 | 
						|
                Pour rendre les choses encore pire, à chaque fois que
 | 
						|
                le format de log change ou bien que la méthode de création
 | 
						|
                des sessions change, nous devons réécrire une partie
 | 
						|
                des tests alors même qu'ils ne testent pas ces parties
 | 
						|
                du système. Nous sommes en train de préparer
 | 
						|
                le cauchemar pour les développeurs de ces autres classes.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                A la place, voici la méthode complète pour le test
 | 
						|
                avec un peu de magie via les objets fantaisie...
 | 
						|
<pre>
 | 
						|
Mock::generate('Session');
 | 
						|
Mock::generate('SessionPool');
 | 
						|
Mock::generate('Log');
 | 
						|
 | 
						|
class LoggingSessionPoolTest extends UnitTestCase {
 | 
						|
    ...
 | 
						|
    function testFindSessionLogging() {<strong>
 | 
						|
        $session = &new MockSession($this);
 | 
						|
        $pool = &new MockSessionPool($this);
 | 
						|
        $pool->setReturnReference('findSession', $session);
 | 
						|
        $pool->expectOnce('findSession', array('abc'));
 | 
						|
        
 | 
						|
        $log = &new MockLog($this);
 | 
						|
        $log->expectOnce('message', array('Starting session abc'));
 | 
						|
        
 | 
						|
        $logging_pool = &new LoggingSessionPool($pool, $log);
 | 
						|
        $this->assertReference($logging_pool->findSession('abc'), $session);
 | 
						|
        $pool->tally();
 | 
						|
        $log->tally();</strong>
 | 
						|
    }
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                Commençons par écrire une session simulacre.
 | 
						|
                Pas la peine d'être trop pointilleux avec
 | 
						|
                celle-ci puisque la vérification de la session
 | 
						|
                désirée est effectuée ailleurs. Nous avons
 | 
						|
                juste besoin de vérifier qu'il s'agit de
 | 
						|
                la même que celle qui vient du groupe commun des sessions.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                <span class="new_code">findSession()</span> est un méthode fabrique
 | 
						|
                dont la simulation est décrite <a href="#stub">plus haut</a>.
 | 
						|
                Le point de départ vient avec le premier appel
 | 
						|
                <span class="new_code">expectOnce()</span>. Cette ligne indique
 | 
						|
                qu'à chaque fois que <span class="new_code">findSession()</span>
 | 
						|
                est invoqué sur l'objet fantaisie, il vérifiera
 | 
						|
                les arguments entrant. S'il ne reçoit
 | 
						|
                que la chaîne "abc" en tant qu'argument
 | 
						|
                alors un succès est envoyé au testeur unitaire,
 | 
						|
                sinon c'est un échec qui est généré.
 | 
						|
                Il s'agit là de la partie qui teste si nous avons bien
 | 
						|
                la bonne session. La liste des arguments suit
 | 
						|
                une format identique à celui qui précise les valeurs renvoyées.
 | 
						|
                Vous pouvez avoir des jokers et des séquences
 | 
						|
                et l'ordre de l'évaluation restera le même.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Si l'appel n'est jamais effectué alors n'est généré
 | 
						|
                ni le succès, ni l'échec. Pour contourner cette limitation,
 | 
						|
                nous devons dire à l'objet fantaisie que le test est terminé :
 | 
						|
                il pourra alors décider si les attentes ont été répondues.
 | 
						|
                L'assertion du testeur unitaire de ceci est déclenchée
 | 
						|
                par l'appel <span class="new_code">tally()</span> à la fin du test.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Nous utilisons le même modèle pour mettre sur pied
 | 
						|
                le loggueur fantaisie. Nous lui indiquons que <span class="new_code">message()</span>
 | 
						|
                devrait être invoqué une fois et une fois seulement
 | 
						|
                avec l'argument "Starting session abc".
 | 
						|
                En testant les arguments d'appel, plutôt que ceux de sortie du loggueur,
 | 
						|
                nous isolons le test de tout modification dans le loggueur.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Nous commençons le lancement nos tests à la création
 | 
						|
                du nouveau <span class="new_code">LoggingSessionPool</span>
 | 
						|
                et nous l'alimentons avec nos objets fantaisie juste créés.
 | 
						|
                Désormais tout est sous contrôle. Au final nous confirmons
 | 
						|
                que le <span class="new_code">$session</span> donné au décorateur est bien
 | 
						|
                celui reçu et prions les objets fantaisie de lancer leurs
 | 
						|
                tests de comptage d'appel interne avec les appels <span class="new_code">tally()</span>.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Il y a encore pas mal de code de test, mais ce code est très strict.
 | 
						|
                S'il vous semble encore terrifiant il l'est bien moins
 | 
						|
                que si nous avions essayé sans les objets fantaisie
 | 
						|
                et ce test en particulier, interactions plutôt que résultat,
 | 
						|
                est toujours plus difficile à mettre en place.
 | 
						|
                Le plus souvent vous aurez besoin de tester des situations
 | 
						|
                plus complexes sans ce niveau ni cette précision.
 | 
						|
                En outre une partie peut être remaniée avec la méthode
 | 
						|
                de scénario de test <span class="new_code">setUp()</span>.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Voici la liste complète des attentes que vous pouvez
 | 
						|
                placer sur un objet fantaisie avec
 | 
						|
                <a href="http://www.lastcraft.com/simple_test.php">SimpleTest</a>...
 | 
						|
                <table>
 | 
						|
<thead>
 | 
						|
                    <tr>
 | 
						|
<th>Attente</th>
 | 
						|
<th>Nécessite <span class="new_code">tally()</span>
 | 
						|
</th>
 | 
						|
</tr>
 | 
						|
                    </thead>
 | 
						|
<tbody>
 | 
						|
<tr>
 | 
						|
                        <td><span class="new_code">expectArguments($method, $args)</span></td>
 | 
						|
                        <td style="text-align: center">Non</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectArgumentsAt($timing, $method, $args)</span></td>
 | 
						|
                        <td style="text-align: center">Non</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectCallCount($method, $count)</span></td>
 | 
						|
                        <td style="text-align: center">Oui</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectMaximumCallCount($method, $count)</span></td>
 | 
						|
                        <td style="text-align: center">Non</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectMinimumCallCount($method, $count)</span></td>
 | 
						|
                        <td style="text-align: center">Oui</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectNever($method)</span></td>
 | 
						|
                        <td style="text-align: center">Non</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectOnce($method, $args)</span></td>
 | 
						|
                        <td style="text-align: center">Oui</td>
 | 
						|
                    </tr>
 | 
						|
                    <tr>
 | 
						|
                        <td><span class="new_code">expectAtLeastOnce($method, $args)</span></td>
 | 
						|
                        <td style="text-align: center">Oui</td>
 | 
						|
                    </tr>
 | 
						|
                </tbody>
 | 
						|
</table>
 | 
						|
                Où les paramètres sont...
 | 
						|
                <dl>
 | 
						|
                    <dt class="new_code">$method</dt>
 | 
						|
                    <dd>Le nom de la méthode, sous la forme d'une chaîne,
 | 
						|
                    à laquelle la condition doit être appliquée.</dd>
 | 
						|
                    <dt class="new_code">$args</dt>
 | 
						|
                    <dd>
 | 
						|
                        Les arguments sous la forme d'une liste.
 | 
						|
                        Les jokers peuvent être inclus de la même manière
 | 
						|
                        qu'avec <span class="new_code">setReturn()</span>.
 | 
						|
                        Cet argument est optionnel pour <span class="new_code">expectOnce()</span>
 | 
						|
                        et <span class="new_code">expectAtLeastOnce()</span>.
 | 
						|
                    </dd>
 | 
						|
                    <dt class="new_code">$timing</dt>
 | 
						|
                    <dd>
 | 
						|
                        Le seul point dans le temps pour tester
 | 
						|
                        la condition. Le premier appel commence à zéro.
 | 
						|
                    </dd>
 | 
						|
                    <dt class="new_code">$count</dt>
 | 
						|
                    <dd>Le nombre d'appels attendu.</dd>
 | 
						|
                </dl>
 | 
						|
                La méthode <span class="new_code">expectMaximumCallCount()</span>
 | 
						|
                est légèrement différente dans le sens où elle ne pourra
 | 
						|
                générer qu'un échec. Elle reste silencieuse
 | 
						|
                si la limite n'est jamais atteinte.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Par ailleurs si vous avez just un appel dans votre test,
 | 
						|
                vérifiez bien que vous utiliser
 | 
						|
                <span class="new_code">expectOnce</span>.<br>
 | 
						|
                Utiliser <span class="new_code">$mocked->expectAt(0, 'method', 'args);</span>
 | 
						|
                tout seul ne sera pas pris en compte :
 | 
						|
                la vérification des arguments et le comptage total
 | 
						|
                sont pour l'instant encore indépendant.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Comme avec les assertions dans les scénarios de test,
 | 
						|
                toutes ces attentes peuvent accepter une surcharge de
 | 
						|
                message sous la forme d'un paramètre supplémentaire.
 | 
						|
                Par ailleurs le message d'échec original peut être inclus
 | 
						|
                dans le résultat avec "%s".
 | 
						|
            </p>
 | 
						|
        
 | 
						|
        <p><a class="target" name="approches"><h2>D'autres approches</h2></a></p>
 | 
						|
            <p>
 | 
						|
                Il existe trois approches pour créer des objets
 | 
						|
                fantaisie en comprenant celle utilisée par SimpleTest.
 | 
						|
                Les coder à la main en utilisant une classe de base,
 | 
						|
                les générer dans un fichier ou les générer dynamiquement à la volée.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Les objets fantaisie générés avec
 | 
						|
                <a href="simple_test.html">SimpleTest</a> sont dynamiques.
 | 
						|
                Ils sont créés à l'exécution dans la mémoire,
 | 
						|
                grâce à <span class="new_code">eval()</span>, plutôt qu'écrits dans un fichier.
 | 
						|
                Cette opération les rend facile à créer,
 | 
						|
                en une seule ligne, surtout par rapport à leur création
 | 
						|
                à la main dans une hiérarchie de classe parallèle.
 | 
						|
                Le problème avec ce comportement tient généralement
 | 
						|
                dans la mise en place des tests proprement dits.
 | 
						|
                Si les objets originaux changent les versions fantaisie
 | 
						|
                sur lesquels reposent les tests, une désynchronisation peut subvenir.
 | 
						|
                Cela peut aussi arriver avec l'approche en hiérarchie parallèle,
 | 
						|
                mais c'est détecté beaucoup plus vite.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Bien sûr, la solution est d'ajouter de véritables tests d'intégration.
 | 
						|
                Vous n'en avez pas besoin de beaucoup
 | 
						|
                et le côté pratique des objets fantaisie fait plus
 | 
						|
                que compenser la petite dose de test supplémentaire.
 | 
						|
                Vous ne pouvez pas avoir confiance dans du code qui
 | 
						|
                ne serait testé que par des objets fantaisie.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Si vous restez déterminé de construire des librairies
 | 
						|
                statiques d'objets fantaisie parce que vous souhaitez
 | 
						|
                émuler un comportement très spécifique,
 | 
						|
                vous pouvez y parvenir grâce au générateur de classe de SimpleTest.
 | 
						|
                Dans votre fichier librairie, par exemple
 | 
						|
                <em>mocks/connection.php</em> pour une connexion à une base de données,
 | 
						|
                créer un objet fantaisie et provoquer l'héritage
 | 
						|
                pour hériter pour surcharger des méthodes spéciales
 | 
						|
                ou ajouter des préréglages...
 | 
						|
<pre>
 | 
						|
<?php
 | 
						|
    require_once('simpletest/mock_objects.php');
 | 
						|
    require_once('../classes/connection.php');
 | 
						|
<strong>
 | 
						|
    Mock::generate('Connection', 'BasicMockConnection');
 | 
						|
    class MockConnection extends BasicMockConnection {
 | 
						|
        function MockConnection(&$test, $wildcard = '*') {
 | 
						|
            $this->BasicMockConnection($test, $wildcard);
 | 
						|
            $this->setReturn('query', false);
 | 
						|
        }
 | 
						|
    }</strong>
 | 
						|
?>
 | 
						|
</pre>
 | 
						|
                L'appel <span class="new_code">generate</span> dit au générateur de classe
 | 
						|
                d'en créer une appelée <span class="new_code">BasicMockConnection</span>
 | 
						|
                plutôt que la plus courante <span class="new_code">MockConnection</span>.
 | 
						|
                Ensuite nous héritons à partir de celle-ci pour obtenir
 | 
						|
                notre version de <span class="new_code">MockConnection</span>.
 | 
						|
                En interceptant de cette manière nous pouvons ajouter
 | 
						|
                un comportement, ici transformer la valeur par défaut de
 | 
						|
                <span class="new_code">query()</span> en "false".
 | 
						|
                En utilisant le nom par défaut nous garantissons
 | 
						|
                que le générateur de classe fantaisie n'en recréera
 | 
						|
                pas une autre différente si il est invoqué ailleurs
 | 
						|
                dans les tests. Il ne créera jamais de classe
 | 
						|
                si elle existe déjà. Aussi longtemps que le fichier
 | 
						|
                ci-dessus est inclus avant alors tous les tests qui
 | 
						|
                généraient <span class="new_code">MockConnection</span> devraient
 | 
						|
                utiliser notre version à présent. Par contre si
 | 
						|
                nous avons une erreur dans l'ordre et que la librairie
 | 
						|
                de fantaisie en crée une d'abord alors la création
 | 
						|
                de la classe échouera tout simplement.
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Utiliser cette astuce si vous vous trouvez avec beaucoup
 | 
						|
                de comportement en commun sur les objets fantaisie
 | 
						|
                ou si vous avez de fréquents problèmes d'intégration
 | 
						|
                plus tard dans les étapes de test.
 | 
						|
            </p>
 | 
						|
        
 | 
						|
        <p><a class="target" name="autres_testeurs"><h2>Je pense que SimpleTest pue !</h2></a></p>
 | 
						|
            <p>
 | 
						|
                Mais au moment d'écrire ces lignes c'est le seul
 | 
						|
                à gérer les objets fantaisie, donc vous êtes bloqué avec lui ?
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Non, pas du tout.
 | 
						|
                <a href="simple_test.html">SimpleTest</a> est une boîte à outils
 | 
						|
                et parmi ceux-ci on trouve les objets fantaisie
 | 
						|
                qui peuvent être utilisés indépendamment.
 | 
						|
                Supposons que vous avez votre propre testeur unitaire favori
 | 
						|
                et que tous vos tests actuels l'utilisent.
 | 
						|
                Prétendez que vous avez appelé votre tester unitaire PHPUnit
 | 
						|
                (c'est ce que tout le monde a fait) et que la classe principale
 | 
						|
                de test ressemble à...
 | 
						|
<pre>
 | 
						|
class PHPUnit {
 | 
						|
    function PHPUnit() {
 | 
						|
    }
 | 
						|
    
 | 
						|
    function assertion($message, $assertion) {
 | 
						|
    }
 | 
						|
    ...
 | 
						|
}
 | 
						|
</pre>
 | 
						|
                La seule chose que la méthode <span class="new_code">assertion()</span> réalise,
 | 
						|
                c'est de préparer une sortie embellie alors le paramètre boolien
 | 
						|
                de l'assertion sert à déterminer s'il s'agit d'une erreur ou d'un succès.
 | 
						|
                Supposons qu'elle est utilisée de la manière suivante...
 | 
						|
<pre>
 | 
						|
$unit_test = new PHPUnit();
 | 
						|
$unit_test>assertion('I hope this file exists', file_exists('my_file'));
 | 
						|
</pre>
 | 
						|
                Comment utiliser les objets fantaisie avec ceci ?
 | 
						|
            </p>
 | 
						|
            <p>
 | 
						|
                Il y a une méthode protégée sur la classe de base
 | 
						|
                des objets fantaisie : elle s'appelle <span class="new_code">_assertTrue()</span>.
 | 
						|
                En surchargeant cette méthode nous pouvons utiliser
 | 
						|
                notre propre format d'assertion.
 | 
						|
                Nous commençons avec une sous-classe, dans <em>my_mock.php</em>...
 | 
						|
<pre>
 | 
						|
<strong><?php
 | 
						|
    require_once('simpletest/mock_objects.php');
 | 
						|
    
 | 
						|
    class MyMock extends SimpleMock() {
 | 
						|
        function MyMock(&$test, $wildcard) {
 | 
						|
            $this->SimpleMock($test, $wildcard);
 | 
						|
        }
 | 
						|
        
 | 
						|
        function _assertTrue($assertion, $message) {
 | 
						|
            $test = &$this->getTest();
 | 
						|
            $test->assertion($message, $assertion);
 | 
						|
        }
 | 
						|
    }
 | 
						|
?></strong>
 | 
						|
</pre>
 | 
						|
                Maintenant une instance de <span class="new_code">MyMock</span>
 | 
						|
                créera un objet qui parle le même langage que votre testeur.
 | 
						|
                Bien sûr le truc c'est que nous créons jamais un tel objet :
 | 
						|
                le générateur s'en chargera. Nous avons juste besoin
 | 
						|
                d'une ligne de code supplémentaire pour dire au générateur
 | 
						|
                d'utiliser vos nouveaux objets fantaisie...
 | 
						|
<pre>
 | 
						|
<?php
 | 
						|
    require_once('simpletst/mock_objects.php');
 | 
						|
    
 | 
						|
    class MyMock extends SimpleMock() {
 | 
						|
        function MyMock($test, $wildcard) {
 | 
						|
            $this->SimpleMock(&$test, $wildcard);
 | 
						|
        }
 | 
						|
        
 | 
						|
        function _assertTrue($assertion, $message , &$test) {
 | 
						|
            $test->assertion($message, $assertion);
 | 
						|
        }
 | 
						|
    }<strong>
 | 
						|
    SimpleTestOptions::setMockBaseClass('MyMock');</strong>
 | 
						|
?>
 | 
						|
</pre>
 | 
						|
                A partir de maintenant vous avez juste à inclure
 | 
						|
                <em>my_mock.php</em> à la place de la version par défaut
 | 
						|
                <em>simple_mock.php</em> et vous pouvez introduire
 | 
						|
                des objets fantaisie dans votre suite de tests existants.
 | 
						|
            </p>
 | 
						|
        
 | 
						|
    </div>
 | 
						|
        References and related information...
 | 
						|
        <ul>
 | 
						|
<li>
 | 
						|
            L'article originel sur
 | 
						|
            <a href="http://www.mockobjects.com/">les objets fantaisie</a>.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            La page du projet SimpleTest sur
 | 
						|
            <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>.
 | 
						|
        </li>
 | 
						|
<li>
 | 
						|
            La page d'accueil de SimpleTest sur
 | 
						|
            <a href="http://www.lastcraft.com/simple_test.php">LastCraft</a>.
 | 
						|
        </li>
 | 
						|
</ul>
 | 
						|
<div class="menu_back"><div class="menu">
 | 
						|
<a href="index.html">SimpleTest</a>
 | 
						|
                |
 | 
						|
                <a href="overview.html">Overview</a>
 | 
						|
                |
 | 
						|
                <a href="unit_test_documentation.html">Unit tester</a>
 | 
						|
                |
 | 
						|
                <a href="group_test_documentation.html">Group tests</a>
 | 
						|
                |
 | 
						|
                <a href="mock_objects_documentation.html">Mock objects</a>
 | 
						|
                |
 | 
						|
                <a href="partial_mocks_documentation.html">Partial mocks</a>
 | 
						|
                |
 | 
						|
                <a href="reporter_documentation.html">Reporting</a>
 | 
						|
                |
 | 
						|
                <a href="expectation_documentation.html">Expectations</a>
 | 
						|
                |
 | 
						|
                <a href="web_tester_documentation.html">Web tester</a>
 | 
						|
                |
 | 
						|
                <a href="form_testing_documentation.html">Testing forms</a>
 | 
						|
                |
 | 
						|
                <a href="authentication_documentation.html">Authentication</a>
 | 
						|
                |
 | 
						|
                <a href="browser_documentation.html">Scriptable browser</a>
 | 
						|
</div></div>
 | 
						|
<div class="copyright">
 | 
						|
            Copyright<br>Marcus Baker 2006
 | 
						|
        </div>
 | 
						|
</body>
 | 
						|
</html>
 |