1
0
mirror of synced 2024-12-14 23:26:04 +03:00
doctrine2/vendor/simpletest/docs/fr/mock_objects_documentation.html
2007-01-28 22:39:21 +00:00

493 lines
31 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">
<h2>
<a href="index.html">SimpleTest</a>
</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>Documentation sur les objets fantaisie</h1>
<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&ocirc;les pendant un sc&eacute;nario de test : acteur et critique.
</p>
<p>
Le comportement d'acteur est celui de simuler des objets difficiles &agrave; initialiser ou trop consommateurs en temps pendant un test. Le cas classique est celui de la connexion &agrave; une base de donn&eacute;es. Mettre sur pied une base de donn&eacute;es de test au lancement de chaque test ralentirait consid&eacute;rablement les tests et en plus exigerait l'installation d'un moteur de base de donn&eacute;es ainsi que des donn&eacute;es sur la machine de test. Si nous pouvons simuler la connexion et renvoyer des donn&eacute;es &agrave; notre guise alors non seulement nous gagnons en pragmatisme sur les tests mais en sus nous pouvons nourrir notre base avec des donn&eacute;es falsifi&eacute;es et voir comment il r&eacute;pond. Nous pouvons simuler une base de donn&eacute;es en suspens ou d'autres cas extr&ecirc;mes sans avoir &agrave; cr&eacute;er une v&eacute;ritable panne de base de donn&eacute;es. En d'autres termes nous pouvons gagner en contr&ocirc;le sur l'environnement de test.
</p>
<p>
Si les objets fantaisie ne se comportaient que comme des acteurs alors on les conna&icirc;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&ocirc;le (en fournissant &agrave; la demande les valeurs requises) mais en plus ils sont aussi sensibles aux messages qui leur sont envoy&eacute;s (par le biais d'attentes). En posant les param&egrave;tres attendus d'une m&eacute;thode ils agissent comme des gardiens : un appel sur eux doit &ecirc;tre r&eacute;alis&eacute; correctement. Si les attentes ne sont pas atteintes ils nous &eacute;pargnent l'effort de l'&eacute;criture d'une assertion de test avec &eacute;chec en r&eacute;alisant cette t&acirc;che &agrave; notre place. Dans le cas d'une connexion &agrave; une base de donn&eacute;es imaginaire ils peuvent tester si la requ&ecirc;te, disons SQL, a bien &eacute;t&eacute; form&eacute; par l'objet qui utilise cette connexion. Mettez-les sur pied avec des attentes assez pr&eacute;cises et vous verrez que vous n'aurez presque plus d'assertion &agrave; &eacute;crire manuellement.
</p>
<p>
<a class="target" name="creation">
<h2>Cr&eacute;er des objets fantaisie</h2>
</a>
</p>
<p>
Comme pour la cr&eacute;ation des bouchons serveur, tout ce dont nous avons besoin c'est d'un classe existante. La fameuse connexion &agrave; une base de donn&eacute;es qui ressemblerait &agrave;...
<pre>
<strong>class DatabaseConnection {
function DatabaseConnection() {
}
function query() {
}
function selectQuery() {
}
}</strong>
</pre>
Cette classe n'a pas encore besoin d'&ecirc;tre impl&eacute;ment&eacute;e. Pour en cr&eacute;er sa version fantaisie nous devons juste inclure la librairie d'objet fantaisie puis lancer le g&eacute;n&eacute;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&eacute;n&egrave;re une classe clone appel&eacute;e <span class="new_code">MockDatabaseConnection</span>. Nous pouvons d&eacute;sormais cr&eacute;er des instances de cette nouvelle classe &agrave; l'int&eacute;rieur m&ecirc;me de notre sc&eacute;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 = &amp;new MockDatabaseConnection($this);
}
}</strong>
</pre>
Contrairement aux bouchons, le constructeur d'une classe fantaisie a besoin d'une r&eacute;f&eacute;rence au sc&eacute;nario de test pour pouvoir transmettre les succ&egrave;s et les &eacute;checs pendant qu'il v&eacute;rifie les attentes. Concr&egrave;tement &ccedil;a veut dire que les objets fantaisie ne peuvent &ecirc;tre utilis&eacute;s qu'au sein d'un sc&eacute;nario de test. Malgr&eacute; tout, cette puissance suppl&eacute;mentaire implique que les bouchons ne sont que rarement utilis&eacute;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&eacute;thodes de l'originale. De la sorte une op&eacute;ration comme <span class="new_code">$connection-&gt;query()</span> est encore possible. Tout comme avec les bouchons, nous pouvons remplacer la valeur nulle renvoy&eacute;e par d&eacute;faut...
<pre>
<strong>$connection-&gt;setReturnValue('query', 37);</strong>
</pre>
D&eacute;sormais &agrave; chaque appel de <span class="new_code">$connection-&gt;query()</span> nous recevons comme r&eacute;sultat 37. Tout comme avec les bouchons nous pouvons utiliser des jokers et surcharger le param&egrave;tre joker. Nous pouvons aussi ajouter des m&eacute;thodes suppl&eacute;mentaires &agrave; l'objet fantaisie lors de sa g&eacute;n&eacute;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&eacute; le m&eacute;canisme <span class="new_code">overload()</span> de PHP pour ajouter des m&eacute;thodes dynamiques. Vous pouvez cr&eacute;er des fantaisies sp&eacute;ciales pour simuler cette situation.
</p>
<p>
Tous les mod&egrave;les disponibles avec les bouchons serveur le sont &eacute;galement avec les objets fantaisie...
<pre>
class Iterator {
function Iterator() {
}
function next() {
}
}
</pre>
Une nouvelle fois, supposons que cet it&eacute;rateur ne retourne que du texte jusqu'au moment o&ugrave; 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 = &amp;new MockIterator($this);
$iterator-&gt;setReturnValue('next', false);
$iterator-&gt;setReturnValueAt(0, 'next', 'First string');
$iterator-&gt;setReturnValueAt(1, 'next', 'Second string');</strong>
...
}
}
</pre>
Au moment du premier appel &agrave; <span class="new_code">next()</span> sur l'it&eacute;rateur fantaisie il renverra tout d'abord "First string", puis ce sera au tour de "Second string" au deuxi&egrave;me appel et ensuite pour tout appel suivant <span class="new_code">false</span> sera renvoy&eacute;. Ces valeurs renvoy&eacute;es successivement sont prioritaires sur la valeur constante retourn&eacute;e. Cette derni&egrave;re est un genre de valeur par d&eacute;faut si vous voulez.
</p>
<p>
Reprenons aussi le conteneur d'information bouchonn&eacute; avec des pairs clef / valeur...
<pre>
class Configuration {
function Configuration() {
}
function getValue($key) {
}
}
</pre>
Il s'agit l&agrave; d'une situation classique d'utilisation d'objets fantaisie &eacute;tant donn&eacute; que la configuration peut varier grandement de machine &agrave; machine : &ccedil;a contraint fortement la fiabilit&eacute; de nos tests si nous l'utilisons directement. Le probl&egrave;me est que toutes les donn&eacute;es nous parviennent &agrave; travers la m&eacute;thode <span class="new_code">getValue()</span> et que nous voulons des r&eacute;sultats diff&eacute;rents pour des clefs diff&eacute;rentes. Heureusement les objets fantaisie ont un syst&egrave;me de filtrage...
<pre>
<strong>$config = &amp;new MockConfiguration($this);
$config-&gt;setReturnValue('getValue', 'primary', array('db_host'));
$config-&gt;setReturnValue('getValue', 'admin', array('db_user'));
$config-&gt;setReturnValue('getValue', 'secret', array('db_password'));</strong>
</pre>
Le param&egrave;tre en plus est une liste d'arguments &agrave; faire correspondre. Dans ce cas nous essayons de faire correspondre un unique argument : en l'occurrence la clef recherch&eacute;e. Maintenant que la m&eacute;thode <span class="new_code">getValue()</span> est invoqu&eacute;e sur l'objet fantaisie...
<pre>
$config-&gt;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&egrave;s les autres jusqu'au moment o&ugrave; une correspondance exacte est atteinte.
</p>
<p>
Il y a des fois o&ugrave; vous souhaitez qu'un objet sp&eacute;cifique soit servi par la fantaisie plut&ocirc;t qu'une copie. De nouveau c'est identique au m&eacute;canisme des bouchons serveur...
<pre>
class Thing {
}
class Vector {
function Vector() {
}
function get($index) {
}
}
</pre>
Dans ce cas vous pouvez placer une r&eacute;f&eacute;rence dans la liste renvoy&eacute;e par l'objet fantaisie...
<pre>
$thing = new Thing();<strong>
$vector = &amp;new MockVector($this);
$vector-&gt;setReturnReference('get', $thing, array(12));</strong>
</pre>
Avec cet arrangement vous savez qu'&agrave; chaque appel de <span class="new_code">$vector-&gt;get(12)</span> le m&ecirc;me <span class="new_code">$thing</span> sera renvoy&eacute;.
</p>
<p>
<a class="target" name="attentes">
<h2>Objets fantaisie en critique</h2>
</a>
</p>
<p>
M&ecirc;me si les bouchons serveur vous isolent du d&eacute;sordre du monde r&eacute;el, il ne s'agit l&agrave; que de la moiti&eacute; du b&eacute;n&eacute;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> &agrave; laquelle nous allons ajouter une fonction de log. Plut&ocirc;t que de complexifier la classe originale, nous souhaitons ajouter ce comportement avec un d&eacute;corateur (GOF). Pour l'instant le code de <span class="new_code">SessionPool</span> ressemble &agrave;...
<pre>
<strong>class SessionPool {
function SessionPool() {
...
}
function &amp;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(&amp;$session_pool, &amp;$log) {
...
}
function &amp;findSession(\$cookie) {
...
}
...
}</strong>
</pre>
Dans tout ceci, la seule classe &agrave; tester est <span class="new_code">LoggingSessionPool</span>. En particulier, nous voulons v&eacute;rifier que la m&eacute;thode <span class="new_code">findSession()</span> est appel&eacute;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 &agrave; faire dans un sc&eacute;nario de test conventionnel :
<ol>
<li>Cr&eacute;er un objet de log.</li>
<li>Indiquer le r&eacute;pertoire d'&eacute;criture du fichier de log.</li>
<li>Modifier les droits sur le r&eacute;pertoire pour pouvoir y &eacute;crire le fichier.</li>
<li>Cr&eacute;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 sessions (en esp&eacute;rant qu'il existe un accesseur !).</li>
<li>Lever une assertion de test pour v&eacute;rifier que cet identifiant correspond bien au cookie.</li>
<li>Lire la derni&egrave;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&eacute;rifier que le message de session est bien dans le texte.</li>
</ol>
Pas &eacute;tonnant que les d&eacute;veloppeurs d&eacute;testent &eacute;crire des tests quand ils sont aussi ingrats. Pour rendre les choses encore pire, &agrave; chaque fois que le format de log change ou bien que la m&eacute;thode de cr&eacute;ation des sessions change, nous devons r&eacute;&eacute;crire une partie des tests alors m&ecirc;me qu'ils ne testent pas ces parties du syst&egrave;me. Nous sommes en train de pr&eacute;parer le cauchemar pour les d&eacute;veloppeurs de ces autres classes.
</p>
<p>
A la place, voici la m&eacute;thode compl&egrave;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 = &amp;new MockSession($this);
$pool = &amp;new MockSessionPool($this);
$pool-&gt;setReturnReference('findSession', $session);
$pool-&gt;expectOnce('findSession', array('abc'));
$log = &amp;new MockLog($this);
$log-&gt;expectOnce('message', array('Starting session abc'));
$logging_pool = &amp;new LoggingSessionPool($pool, $log);
$this-&gt;assertReference($logging_pool-&gt;findSession('abc'), $session);
$pool-&gt;tally();
$log-&gt;tally();</strong>
}
}
</pre>
Commen&ccedil;ons par &eacute;crire une session simulacre. Pas la peine d'&ecirc;tre trop pointilleux avec celle-ci puisque la v&eacute;rification de la session d&eacute;sir&eacute;e est effectu&eacute;e ailleurs. Nous avons juste besoin de v&eacute;rifier qu'il s'agit de la m&ecirc;me que celle qui vient du groupe commun des sessions.
</p>
<p>
<span class="new_code">findSession()</span> est un m&eacute;thode fabrique dont la simulation est d&eacute;crite <a href="#stub">plus haut</a>. Le point de d&eacute;part vient avec le premier appel <span class="new_code">expectOnce()</span>. Cette ligne indique qu'&agrave; chaque fois que <span class="new_code">findSession()</span> est invoqu&eacute; sur l'objet fantaisie, il v&eacute;rifiera les arguments entrant. S'il ne re&ccedil;oit que la cha&icirc;ne "abc" en tant qu'argument alors un succ&egrave;s est envoy&eacute; au testeur unitaire, sinon c'est un &eacute;chec qui est g&eacute;n&eacute;r&eacute;. Il s'agit l&agrave; de la partie qui teste si nous avons bien la bonne session. La liste des arguments suit une format identique &agrave; celui qui pr&eacute;cise les valeurs renvoy&eacute;es. Vous pouvez avoir des jokers et des s&eacute;quences et l'ordre de l'&eacute;valuation restera le m&ecirc;me.
</p>
<p>
Si l'appel n'est jamais effectu&eacute; alors n'est g&eacute;n&eacute;r&eacute; ni le succ&egrave;s, ni l'&eacute;chec. Pour contourner cette limitation, nous devons dire &agrave; l'objet fantaisie que le test est termin&eacute; : il pourra alors d&eacute;cider si les attentes ont &eacute;t&eacute; r&eacute;pondues. L'assertion du testeur unitaire de ceci est d&eacute;clench&eacute;e par l'appel <span class="new_code">tally()</span> &agrave; la fin du test.
</p>
<p>
Nous utilisons le m&ecirc;me mod&egrave;le pour mettre sur pied le loggueur fantaisie. Nous lui indiquons que <span class="new_code">message()</span> devrait &ecirc;tre invoqu&eacute; une fois et une fois seulement avec l'argument "Starting session abc". En testant les arguments d'appel, plut&ocirc;t que ceux de sorite du loggueur, nous isolons le test de tout modification dans le loggueur.
</p>
<p>
Nous commen&ccedil;ons le lancement nos tests &agrave; la cr&eacute;ation du nouveau <span class="new_code">LoggingSessionPool</span> et nous l'alimentons avec nos objets fantaisie juste cr&eacute;&eacute;s. D&eacute;sormais tout est sous contr&ocirc;le. Au final nous confirmons que le <span class="new_code">$session</span> donn&eacute; au d&eacute;corateur est bien celui re&ccedil;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&egrave;s strict. S'il vous semble encore terrifiant il l'est bien moins que si nous avions essay&eacute; sans les objets fantaisie et ce test en particulier, interactions plut&ocirc;t que r&eacute;sultat, est toujours plus difficile &agrave; mettre en place. Le plus souvent vous aurez besoin de tester des situations plus complexes sans ce niveau ni cette pr&eacute;cision. En outre une partie peut &ecirc;tre remani&eacute;e avec la m&eacute;thode de sc&eacute;nario de test <span class="new_code">setUp()</span>.
</p>
<p>
Voici la liste compl&egrave;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&eacute;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&ugrave; les param&egrave;tres sont...
<dl>
<dt class="new_code">$method</dt>
<dd>Le nom de la m&eacute;thode, sous la forme d'une cha&icirc;ne, &agrave; laquelle la condition doit &ecirc;tre appliqu&eacute;e.</dd>
<dt class="new_code">$args</dt>
<dd>
Les arguments sous la forme d'une liste. Les jokers peuvent &ecirc;tre inclus de la m&ecirc;me mani&egrave;re qu'avec <span class="new_code">setReturn()</span>. Cet argument est optionel 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 &agrave; z&eacute;ro.
</dd>
<dt class="new_code">$count</dt>
<dd>Le nombre d'appels attendu.</dd>
</dl>
La m&eacute;thode <span class="new_code">expectMaximumCallCount()</span> est l&eacute;g&egrave;rement diff&eacute;rente dans le sens o&ugrave; elle ne pourra g&eacute;n&eacute;rer qu'un &eacute;chec. Elle reste silencieuse si la limite n'est jamais atteinte.
</p>
<p>
Comme avec les assertions dans les sc&eacute;narios de test, toutes ces attentes peuvent accepter une surcharge de message sous la forme d'un param&egrave;tre suppl&eacute;mentaire. Par ailleurs le message d'&eacute;chec original peut &ecirc;tre inclus dans le r&eacute;sultat avec "%s".
</p>
<p>
<a class="target" name="approches">
<h2>D'autres approches</h2>
</a>
</p>
<p>
Il existe trois approches pour cr&eacute;er des objets fantaisie en comprenant celle utiliser par SimpleTest. Les coder &agrave; la main en utilisant une classe de base, les g&eacute;n&eacute;rer dans un fichier ou les g&eacute;n&eacute;rer dynamiquement &agrave; la vol&eacute;e.
</p>
<p>
Les objets fantaisie g&eacute;n&eacute;r&eacute;s avec <a href="simple_test.html">SimpleTest</a> sont dynamiques. Ils sont cr&eacute;&eacute;s &agrave; l'ex&eacute;cution dans la m&eacute;moire, gr&acirc;ce &agrave; <span class="new_code">eval()</span>, plut&ocirc;t qu'&eacute;crit dans un fichier. Cette op&eacute;ration les rend facile &agrave; cr&eacute;er, en une seule ligne, surtout par rapport &agrave; leur cr&eacute;ation &agrave; la main dans une hi&eacute;rarchie de classe parall&egrave;le. Le probl&egrave;me avec ce comportement tient g&eacute;n&eacute;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&eacute;synchronisation peut subvenir. Cela peut aussi arriver avec l'approche en hi&eacute;rarchie parall&egrave;le, mais c'est d&eacute;tect&eacute; beaucoup plus vite.
</p>
<p>
Bien s&ucirc;r, la solution est d'ajouter de v&eacute;ritables tests d'int&eacute;gration. Vous n'en avez pas besoin de beaucoup et le c&ocirc;t&eacute; pratique des objets fantaisie fait plus que compenser la petite dose de test suppl&eacute;mentaire. Vous ne pouvez pas avoir confiance dans du code qui ne serait test&eacute; que par des objets fantaisie.
</p>
<p>
Si vous restez d&eacute;termin&eacute; de construire des librairies statiques d'objets fantaisie parce que vous souhaitez &eacute;muler un comportement tr&egrave;s sp&eacute;cifique, vous pouvez y parvenir gr&acirc;ce au g&eacute;n&eacute;rateur de classe de SimpleTest. Dans votre fichier librairie, par exemple <em>mocks/connection.php</em> pour une connexion &agrave; une base de donn&eacute;es, cr&eacute;er un objet fantaisie et provoquer m'h&eacute;ritage pour h&eacute;riter pour surcharger des m&eacute;thodes sp&eacute;ciales ou ajouter des pr&eacute;r&eacute;glages...
<pre>
&lt;?php
require_once('simpletest/mock_objects.php');
require_once('../classes/connection.php');
<strong>
Mock::generate('Connection', 'BasicMockConnection');
class MockConnection extends BasicMockConnection {
function MockConnection(&amp;$test, $wildcard = '*') {
$this-&gt;BasicMockConnection($test, $wildcard);
$this-&gt;setReturn('query', false);
}
}</strong>
?&gt;
</pre>
L'appel <span class="new_code">generate</span> dit au g&eacute;n&eacute;rateur de classe d'en cr&eacute;er une appel&eacute;e <span class="new_code">BasicMockConnection</span> plut&ocirc;t que la plus courante <span class="new_code">MockConnection</span>. Ensuite nous h&eacute;ritons &agrave; partir de celle-ci pour obtenir notre version de <span class="new_code">MockConnection</span>. En interceptant de cette mani&egrave;re nous pouvons ajouter un comportement, ici transformer la valeur par d&eacute;faut de <span class="new_code">query()</span> en "false".
En utilisant le nom par d&eacute;faut nous garantissons que le g&eacute;n&eacute;rateur de classe fantaisie n'en recr&eacute;era pas une autre diff&eacute;rente si il est invoqu&eacute; ailleurs dans les tests. Il ne cr&eacute;era jamais de classe si elle existe d&eacute;j&agrave;. Aussi longtemps que le fichier ci-dessus est inclus avant alors tous les tests qui g&eacute;n&eacute;raient <span class="new_code">MockConnection</span> devraient utiliser notre version &agrave; pr&eacute;sent. Par contre si nous avons une erreur dans l'ordre et que la librairie de fantaisie en cr&eacute;e une d'abord alors la cr&eacute;ation de la classe &eacute;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&eacute;quents probl&egrave;mes d'int&eacute;gration plus tard dans les &eacute;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'&eacute;crire ces lignes c'est le seul &agrave; g&eacute;rer les objets fantaisie, donc vous &ecirc;tes bloqu&eacute; avec lui ?
</p>
<p>
Non, pas du tout.
<a href="simple_test.html">SimpleTest</a> est une bo&icirc;te &agrave; outils et parmi ceux-ci on trouve les objets fantaisie qui peuvent &ecirc;tre utilis&eacute;s ind&eacute;pendamment. Supposons que vous avez votre propre testeur unitaire favori et que tous vos tests actuels l'utilisent. Pr&eacute;tendez que vous avez appel&eacute; votre tester unitaire PHPUnit (c'est ce que tout le monde a fait) et que la classe principale de test ressemble &agrave;...
<pre>
class PHPUnit {
function PHPUnit() {
}
function assertion($message, $assertion) {
}
...
}
</pre>
La seule chose que la m&eacute;thode <span class="new_code">assertion()</span> r&eacute;alise, c'est de pr&eacute;parer une sortie embellie alors le param&egrave;tre boolien de l'assertion sert &agrave; d&eacute;terminer s'il s'agit d'une erreur ou d'un succ&egrave;s. Supposons qu'elle est utilis&eacute;e de la mani&egrave;re suivante...
<pre>
$unit_test = new PHPUnit();
$unit_test&gt;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&eacute;thode prot&eacute;g&eacute;e sur la classe de base des objets fantaisie : elle s'appelle <span class="new_code">_assertTrue()</span>. En surchargeant cette m&eacute;thode nous pouvons utiliser notre propre format d'assertion. Nous commen&ccedil;ons avec une sous-classe, dans <em>my_mock.php</em>...
<pre>
<strong>&lt;?php
require_once('simpletest/mock_objects.php');
class MyMock extends SimpleMock() {
function MyMock(&amp;$test, $wildcard) {
$this-&gt;SimpleMock($test, $wildcard);
}
function _assertTrue($assertion, $message) {
$test = &amp;$this-&gt;getTest();
$test-&gt;assertion($message, $assertion);
}
}
?&gt;</strong>
</pre>
Maintenant une instance de <span class="new_code">MyMock</span> cr&eacute;era un objet qui parle le m&ecirc;me langage que votre testeur. Bien s&ucirc;r le truc c'est que nous cr&eacute;ons jamais un tel objet : le g&eacute;n&eacute;rateur s'en chargera. Nous avons juste besoin d'une ligne de code suppl&eacute;mentaire pour dire au g&eacute;n&eacute;rateur d'utiliser vos nouveaux objets fantaisie...
<pre>
&lt;?php
require_once('simpletst/mock_objects.php');
class MyMock extends SimpleMock() {
function MyMock($test, $wildcard) {
$this-&gt;SimpleMock(&amp;$test, $wildcard);
}
function _assertTrue($assertion, $message , &amp;$test) {
$test-&gt;assertion($message, $assertion);
}
}<strong>
SimpleTestOptions::setMockBaseClass('MyMock');</strong>
?&gt;
</pre>
A partir de maintenant vous avez juste &agrave; inclure <em>my_mock.php</em> &agrave; la place de la version par d&eacute;faut <em>simple_mock.php</em> et vous pouvez introduire des objets fantaisie dans votre suite de tests existants.
</p>
</div>
<div class="copyright">
Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
</div>
</body>
</html>