cakephp2-php8/vendors/simpletest/docs/fr/server_stubs_documentation.html
pies da79dff7d7 - merged in Brego's SimpleTest implementation, fixed some of the tests (the Folder test fails to delete one of the test directories on my system, so it's not perfectly clean yet)
- Dispatcher sets a Controller::here variable with the real URL used to access the page, so that tag generators can that use an url (linkTo and formTag for example) use the real url, not guess it from the controller and action names which often fails
- Log class works more reliably and a LogError() shortcut function was added
- Nstring class added, to store string-related functions (there are just four yet, including a random password generator and an string-to-array splitter
- SimpleTest library (with Rephlux) included in /vendors; I've tweaked SimpleScorer::inCli() function, because it didn't work on my setup, it should work everywhere now (it checks for empty REQUEST_METHOD, which should only be empty in CLI)

git-svn-id: https://svn.cakephp.org/repo/trunk/cake@248 3807eeeb-6ff5-0310-8944-8be069107fe0
2005-06-18 23:26:35 +00:00

279 lines
15 KiB
HTML

<html>
<head>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Documentation SimpleTest : les bouchons serveur</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 bouchons serveur</h1>
<div class="content">
<p>
<a class="target" name="quoi">
<h2>Que sont les bouchons serveur ?</h2>
</a>
</p>
<p>
Au d&eacute;part il s'agit d'un mod&egrave;le de conception initi&eacute; par Robert Binder (Testing object-oriented systems: models, patterns, and tools, Addison-Wesley) in 1999. Un bouchon serveur est une simulation d'un objet ou d'un composant. Il doit remplacer exactement un composant dans un syst&egrave;me pour des raisons de testabilit&eacute; ou de prototypage, tout en restant l&eacute;ger. Il permet aux tests de tourner plus rapidement ou alors, si la classe simul&eacute;e n'a pas &eacute;t&eacute; &eacute;crite, juste de fonctionner.
</p>
<p>
<a class="target" name="creation">
<h2>Cr&eacute;er des bouchons serveur</h2>
</a>
</p>
<p>
Nous avons juste besoin d'une classe pr&eacute;existante, par exemple une connexion vers une base de donn&eacute;es qui ressemblerait &agrave;...
<pre>
<strong>class DatabaseConnection {
function DatabaseConnection() {
}
function query() {
}
function selectQuery() {
}
}</strong>
</pre>
La classe n'a m&ecirc;me pas encore besoin d'avoir &eacute;t&eacute; impl&eacute;ment&eacute;e. Pour cr&eacute;er la version bouchonn&eacute;e de cette classe, nous incluons la librairie de bouchon serveur et ex&eacute;cutons le g&eacute;n&eacute;rateur...
<pre>
<strong>require_once('simpletest/mock_objects.php');
require_once('database_connection.php');
Stub::generate('DatabaseConnection');</strong>
</pre>
Est g&eacute;n&eacute;r&eacute; un clone de la classe appel&eacute; <span class="new_code">StubDatabaseConnection</span>. Nous pouvons alors cr&eacute;er des instances de cette nouvelle classe &agrave; l'int&eacute;rieur de notre prototype de script...
<pre>
require_once('simpletest/mock_objects.php');
require_once('database_connection.php');
Stub::generate('DatabaseConnection');
<strong>
$connection = new StubDatabaseConnection();
</strong>
</pre>
La version bouchonn&eacute;e de la classe contient toutes les m&eacute;thodes de l'original de telle sorte qu'une op&eacute;ration comme <span class="new_code">$connection-&gt;query()</span> soit encore l&eacute;gale. La valeur retourn&eacute;e sera <span class="new_code">null</span>, Mais nous pouvons y rem&eacute;dier avec...
<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 obtenons un r&eacute;sultat de 37. Nous pouvons choisir n'importe quelle valeur pour le r&eacute;sultat, par exemple un hash de r&eacute;sultats provenant d'un base de donn&eacute;es imaginaire ou alors une liste d'objets persistants. Peu importe les param&egrave;tres, nous obtenons syst&eacute;matiquement les m&ecirc;me valeurs chaque fois qu'ils ont &eacute;t&eacute; initialis&eacute;s de la sorte : &ccedil;a ne ressemble peut-&ecirc;tre pas &agrave; une r&eacute;ponse convaincante venant d'une connexion vers une base de donn&eacute;es. Mais pour la demi-douzaine de lignes d'une m&eacute;thode de test c'est souvent largement suffisant.
</p>
<p>
<a class="target" name="mod%C3%A8les">
<h2>Mod&egrave;les de simulation</h2>
</a>
</p>
<p>
Sauf que les choses ne sont que rarement aussi simples. Parmi les probl&egrave;mes les plus courants on trouve les it&eacute;rateurs : le renvoi d'une valeur constante peut causer une boucle infini dans l'objet test&eacute;. Pour ceux-ci nous avons besoin de mettre sur pied une suite de valeurs. Prenons par exemple un it&eacute;rateur simple qui ressemble &agrave;...
<pre>
class Iterator {
function Iterator() {
}
function next() {
}
}
</pre>
C'est probablement le plus simple des it&eacute;rateurs possibles. Supposons que cet it&eacute;rateur ne retourne que du texte, jusqu'&agrave; la fin - quand il retourne <span class="new_code">false</span>. Une simulation est possible avec...
<pre>
<strong>Stub::generate('Iterator');
$iterator = new StubIterator();
$iterator-&gt;setReturnValue('next', false);
$iterator-&gt;setReturnValueAt(0, 'next', 'First string');
$iterator-&gt;setReturnValueAt(1, 'next', 'Second string');</strong>
</pre>
A l'appel de <span class="new_code">next()</span> sur l'it&eacute;rateur bouchonn&eacute; il va d'abord renvoyer "First string", puis au second appel c'est "Second string" qui sera renvoy&eacute;. Finalement pour tous les autres appels, il s'agira d'un <span class="new_code">false</span>. Les valeurs renvoy&eacute;es successivement ont priorit&eacute; sur la valeur constante renvoy&eacute;. Cette derni&egrave;re est un genre de valeur par d&eacute;faut.
</p>
<p>
Une autre situation d&eacute;licate est une op&eacute;ration <span class="new_code">get()</span> surcharg&eacute;e. Un exemple ? Un porteur d'information avec des pairs de clef / valeur. Prenons une classe de configuration...
<pre>
class Configuration {
function Configuration() {
}
function getValue($key) {
}
}
</pre>
Il s'agit d'une situation propice &agrave; l'utilisation d'objets bouchon, surtout que la configuration en production d&eacute;pend invariablement de la machine : l'utiliser directement ne va pas nous aider &agrave; maintenir notre confiance dans nos tests. Sauf que le probl&egrave;me tient de ce que toutes les donn&eacute;es proviennent de la m&eacute;thode <span class="new_code">getValue()</span> et que nous voulons des r&eacute;sultats diff&eacute;rents suivant la clef. Par chance les bouchons ont un syst&egrave;me de filtre...
<pre>
<strong>Stub::generate('Configuration');
$config = &amp;new StubConfiguration();
$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>
Ce param&egrave;tre suppl&eacute;mentaire est une liste d'arguments que l'on peut utiliser. Dans ce cas nous essayons d'utiliser un unique argument, &agrave; savoir la clef recherch&eacute;e. Maintenant quand on invoque le bouchon serveur via la m&eacute;thode <span class="new_code">getValue()</span> avec...
<pre>
$config-&gt;getValue('db_user');
</pre>
...il renvoie "admin". Il le trouve en essayant d'assortir successivement les arguments d'entr&eacute;e avec sa liste de ceux de sortie jusqu'au moment o&ugrave; une correspondance exacte est trouv&eacute;e.
</p>
<p>
Vous pouvez d&eacute;finir un argument par d&eacute;faut avec...
<pre>
<strong>
$config-&gt;setReturnValue('getValue', false, array('*'));</strong>
</pre>
Attention ce n'est pas &eacute;quivalent &agrave; initialiser la valeur retourn&eacute;e sans aucun argument.
<pre>
<strong>
$config-&gt;setReturnValue('getValue', false);</strong>
</pre>
Dans le premier cas il acceptera n'importe quel argument, mais exactement un -- pas plus -- est n&eacute;cessaire. Dans le second cas n'importe quel nombre d'arguments fera l'affaire : il agit comme un <cite>catchall</cite> apr&egrave;s tous les correspondances. Prenez garde &agrave; l'ordre : si nous ajoutons un autre param&egrave;tre apr&egrave;s le joker ('*') il sera ignor&eacute; puisque le joker aura &eacute;t&eacute; trouv&eacute; auparavant. Avec des listes de param&egrave;tres complexes l'ordre peut devenir crucial, au risque de perdre des correspondances souhait&eacute;es, masqu&eacute;es par un joker ant&eacute;rieur. Pensez &agrave; mettre d'abord les cas les plus sp&eacute;cifiques si vous n'&ecirc;tes pas s&ucirc;r.
</p>
<p>
Il y a des fois o&ugrave; l'on souhaite qu'un objet sp&eacute;cifique soit servi par le bouchon plut&ocirc;t qu'une simple copie. La s&eacute;mantique de la copie en PHP nous force &agrave; utiliser une autre m&eacute;thode pour cela. Vous &ecirc;tes peut-&ecirc;tre en train de simuler un conteneur par exemple...
<pre>
class Thing {
}
class Vector {
function Vector() {
}
function get($index) {
}
}
</pre>
Dans ce cas vous pouvez mettre une r&eacute;f&eacute;rence dans la liste renvoy&eacute;e par le bouchon...
<pre>
Stub::generate('Vector');
$thing = new Thing();<strong>
$vector = &amp;new StubVector();
$vector-&gt;setReturnReference('get', $thing, array(12));</strong>
</pre>
Avec ce petit arrangement vous vous assurez qu'&agrave; chaque fois que <span class="new_code">$vector-&gt;get(12)</span> est appel&eacute; il renverra le m&ecirc;me <span class="new_code">$thing</span>.
</p>
<p>
Ces trois facteurs, ordre, param&egrave;tres et copie (ou r&eacute;f&eacute;rence), peuvent &ecirc;tre combin&eacute;s orthogonalement. Par exemple...
<pre>
$complex = &amp;new StubComplexThing();
$stuff = new Stuff();<strong>
$complex-&gt;setReturnReferenceAt(3, 'get', $stuff, array('*', 1));</strong>
</pre>
Le <span class="new_code">$stuff</span> ne sera renvoy&eacute; qu'au troisi&egrave;me appel et seulement si deux param&egrave;tres &eacute;taient indiqu&eacute;s, avec la contrainte que le second de ceux-ci soit l'entier 1. N'est-ce pas suffisant pour des situations de prototypage simple ?
</p>
<p>
Un dernier cas critique reste celle d'un objet en cr&eacute;ant un autre, connu sous le nom du mod&egrave;le factory - fabrique. Supposons qu'apr&egrave;s une requ&ecirc;te r&eacute;ussie &agrave; notre base de donn&eacute;es imaginaire, un ensemble de r&eacute;sultats est retourn&eacute; sous la forme d'un it&eacute;rateur, chaque appel &agrave; <span class="new_code">next()</span> donnant un ligne et &agrave; la fin un <span class="new_code">false</span>.
Au premier abord, &ccedil;a donne l'impression d'&ecirc;tre cauchemardesque &agrave; simuler. Alors qu'en fait tout peut &ecirc;tre bouchonn&eacute; en utilisant les m&eacute;canismes ci-dessus.
</p>
<p>
Voici comment...
<pre>
Stub::generate('DatabaseConnection');
Stub::generate('ResultIterator');
class DatabaseTest extends UnitTestCase {
function testUserFinder() {<strong>
$result = &amp;new StubResultIterator();
$result-&gt;setReturnValue('next', false);
$result-&gt;setReturnValueAt(0, 'next', array(1, 'tom'));
$result-&gt;setReturnValueAt(1, 'next', array(3, 'dick'));
$result-&gt;setReturnValueAt(2, 'next', array(6, 'harry'));
$connection = &amp;new StubDatabaseConnection();
$connection-&gt;setReturnValue('query', false);
$connection-&gt;setReturnReference(
'query',
$result,
array('select id, name from users'));</strong>
$finder = &amp;new UserFinder($connection);
$this-&gt;assertIdentical(
$finder-&gt;findNames(),
array('tom', 'dick', 'harry'));
}
}
</pre>
D&eacute;sormais ce n'est que si notre <span class="new_code">$connection</span> est appel&eacute; avec la bonne <span class="new_code">query()</span> que le <span class="new_code">$result</span> sera renvoy&eacute; apr&egrave;s le troisi&egrave;me appel &agrave; <span class="new_code">next()</span>. Cela devrait &ecirc;tre suffisant pour que notre classe <span class="new_code">UserFinder</span>, la classe effectivement test&eacute;e &agrave; ce niveau, puisse s'ex&eacute;cuter comme il faut. Un test tr&egrave;s pr&eacute;cis et pas une seule base de donn&eacute;es &agrave; l'horizon.
</p>
<p>
<a class="target" name="options">
<h2>Options de cr&eacute;ation pour les bouchons</h2>
</a>
</p>
<p>
Il y a d'autres options additionnelles &agrave; la cr&eacute;ation d'un bouchon. Au moment de la g&eacute;n&eacute;ration nous pouvons changer le nom de la classe...
<pre>
<strong>Stub::generate('Iterator', 'MyStubIterator');
$iterator = &amp;new MyStubIterator();
</strong>
</pre>
Pris tout seul ce n'est pas tr&egrave;s utile &eacute;tant donn&eacute; qu'il n'y aurait pas de diff&eacute;rence entre cette classe et celle par d&eacute;faut -- &agrave; part le nom bien entendu. Par contre nous pouvons aussi lui ajouter d'autres m&eacute;thodes qui ne se trouveraient pas dans l'interface originale...
<pre>
class Iterator {
}
<strong>Stub::generate('Iterator', 'PrototypeIterator', array('next', 'isError'));
$iterator = &amp;new PrototypeIterator();
$iterator-&gt;setReturnValue('next', 0);
</strong>
</pre>
Les m&eacute;thodes <span class="new_code">next()</span> et <span class="new_code">isError()</span> peuvent maintenant renvoyer des ensembles de valeurs exactement comme si elles existaient dans la classe originale.
</p>
<p>
Un moyen encore plus &eacute;sot&eacute;rique de modifier les bouchons est de changer le joker utiliser par d&eacute;faut pour la correspondance des param&egrave;tres.
<pre>
<strong>Stub::generate('Connection');
$iterator = &amp;new StubConnection('wild');
$iterator-&gt;setReturnValue('query', array('id' =&gt; 33), array('wild'));
</strong>
</pre>
L'unique raison valable pour effectuer cette op&eacute;ration, c'est quand vous souhaitez tester la cha&icirc;ne "*" sans pour autant l'interpr&eacute;ter comme un "n'importe lequel".
</p>
</div>
<div class="copyright">
Copyright<br>Marcus Baker, Jason Sweat, Perrick Penet 2004
</div>
</body>
</html>