diff options
Diffstat (limited to 'vendors/simpletest/docs/fr/partial_mocks_documentation.html')
-rwxr-xr-x | vendors/simpletest/docs/fr/partial_mocks_documentation.html | 460 |
1 files changed, 460 insertions, 0 deletions
diff --git a/vendors/simpletest/docs/fr/partial_mocks_documentation.html b/vendors/simpletest/docs/fr/partial_mocks_documentation.html new file mode 100755 index 000000000..32a39c760 --- /dev/null +++ b/vendors/simpletest/docs/fr/partial_mocks_documentation.html @@ -0,0 +1,460 @@ +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<title>Documentation SimpleTest : les objets fantaisie partiels</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 partiels</h1> + This page... + <ul> +<li> + <a href="#injection">Le problème de l'injection d'un objet fantaisie</a>. + </li> +<li> + Déplacer la création vers une méthode <a href="#creation">fabrique protégée</a>. + </li> +<li> + <a href="#partiel">L'objet fantaisie partiel</a> génère une sous-classe. + </li> +<li> + Les objets fantaisie partiels <a href="#moins">testent moins qu'une classe</a>. + </li> +</ul> +<div class="content"> + + <p> + Un objet fantaisie partiel n'est ni plus ni moins + qu'un modèle de conception pour soulager un problème spécifique + du test avec des objets fantaisie, celui de placer + des objets fantaisie dans des coins serrés. + Il s'agit d'un outil assez limité et peut-être même + une idée pas si bonne que ça. Elle est incluse dans SimpleTest + pour la simple raison que je l'ai trouvée utile + à plus d'une occasion et qu'elle m'a épargnée + pas mal de travail dans ces moments-là. + </p> + + <p><a class="target" name="injection"><h2>Le problème de l'injection dans un objet fantaisie</h2></a></p> + <p> + Quand un objet en utilise un autre il est très simple + d'y faire circuler une version fantaisie déjà prête + avec ses attentes. Les choses deviennent un peu plus délicates + si un objet en crée un autre et que le créateur est celui + que l'on souhaite tester. Cela revient à dire que l'objet + créé devrait être une fantaisie, mais nous pouvons + difficilement dire à notre classe sous test de créer + un objet fantaisie plutôt qu'un "vrai" objet. + La classe testée ne sait même pas qu'elle travaille dans un environnement de test. + </p> + <p> + Par exemple, supposons que nous sommes en train + de construire un client telnet et qu'il a besoin + de créer une socket réseau pour envoyer ses messages. + La méthode de connexion pourrait ressemble à quelque chose comme... +<pre> +<strong><?php +require_once('socket.php'); + +class Telnet { + ... + function &connect($ip, $port, $username, $password) { + $socket = &new Socket($ip, $port); + $socket->read( ... ); + ... + } +} +?></strong> +</pre> + Nous voudrions vraiment avoir une version fantaisie + de l'objet socket, que pouvons nous faire ? + </p> + <p> + La première solution est de passer la socket en + tant que paramètre, ce qui force la création + au niveau inférieur. Charger le client de cette tâche + est effectivement une bonne approche si c'est possible + et devrait conduire à un remaniement -- de la création + à partir de l'action. En fait, c'est là une des manières + avec lesquels tester en s'appuyant sur des objets fantaisie + vous force à coder des solutions plus resserrées sur leur objectif. + Ils améliorent votre programmation. + </p> + <p> + Voici ce que ça devrait être... +<pre> +<?php +require_once('socket.php'); + +class Telnet { + ... + <strong>function &connect(&$socket, $username, $password) { + $socket->read( ... ); + ... + }</strong> +} +?> +</pre> + Sous-entendu, votre code de test est typique d'un cas + de test avec un objet fantaisie. +<pre> +class TelnetTest extends UnitTestCase { + ... + function testConnection() {<strong> + $socket = &new MockSocket($this); + ... + $telnet = &new Telnet(); + $telnet->connect($socket, 'Me', 'Secret'); + ...</strong> + } +} +</pre> + C'est assez évident que vous ne pouvez descendre que d'un niveau. + Vous ne voudriez pas que votre application de haut niveau + crée tous les fichiers de bas niveau, sockets et autres connexions + à la base de données dont elle aurait besoin. + Elle ne connaîtrait pas les paramètres du constructeur de toute façon. + </p> + <p> + La solution suivante est de passer l'objet créé sous la forme + d'un paramètre optionnel... +<pre> +<?php +require_once('socket.php'); + +class Telnet { + ...<strong> + function &connect($ip, $port, $username, $password, $socket = false) { + if (!$socket) { + $socket = &new Socket($ip, $port); + } + $socket->read( ... );</strong> + ... + return $socket; + } +} +?> +</pre> + Pour une solution rapide, c'est généralement suffisant. + Ensuite le test est très similaire : comme si le paramètre + était transmis formellement... +<pre> +class TelnetTest extends UnitTestCase { + ... + function testConnection() {<strong> + $socket = &new MockSocket($this); + ... + $telnet = &new Telnet(); + $telnet->connect('127.0.0.1', 21, 'Me', 'Secret', &$socket); + ...</strong> + } +} +</pre> + Le problème de cette approche tient dans son manque de netteté. + Il y a du code de test dans la classe principale et aussi + des paramètres transmis dans le scénario de test + qui ne sont jamais utilisés. Il s'agit là d'une approche + rapide et sale, mais qui ne reste pas moins efficace + dans la plupart des situations. + </p> + <p> + Une autre solution encore est de laisser un objet fabrique + s'occuper de la création... +<pre> +<?php +require_once('socket.php'); + +class Telnet {<strong> + function Telnet(&$network) { + $this->_network = &$network; + }</strong> + ... + function &connect($ip, $port, $username, $password) {<strong> + $socket = &$this->_network->createSocket($ip, $port); + $socket->read( ... );</strong> + ... + return $socket; + } +} +?> +</pre> + Il s'agit là probablement de la réponse la plus travaillée + étant donné que la création est maintenant située + dans une petite classe spécialisée. La fabrique réseau + peut être testée séparément et utilisée en tant que fantaisie + quand nous testons la classe telnet... +<pre> +class TelnetTest extends UnitTestCase { + ... + function testConnection() {<strong> + $socket = &new MockSocket($this); + ... + $network = &new MockNetwork($this); + $network->setReturnReference('createSocket', $socket); + $telnet = &new Telnet($network); + $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); + ...</strong> + } +} +</pre> + Le problème reste que nous ajoutons beaucoup de classes + à la bibliothèque. Et aussi que nous utilisons beaucoup + de fabriques ce qui rend notre code un peu moins intuitif. + La solution la plus flexible, mais aussi la plus complexe. + </p> + <p> + Peut-on trouver un juste milieu ? + </p> + + <p><a class="target" name="creation"><h2>Méthode fabrique protégée</h2></a></p> + <p> + Il existe une technique pour palier à ce problème + sans créer de nouvelle classe dans l'application; + par contre elle induit la création d'une sous-classe au moment du test. + Premièrement nous déplaçons la création de la socket dans sa propre méthode... +<pre> +<?php +require_once('socket.php'); + +class Telnet { + ... + function &connect($ip, $port, $username, $password) {<strong> + $socket = &$this->_createSocket($ip, $port);</strong> + $socket->read( ... ); + ... + }<strong> + + function &_createSocket($ip, $port) { + return new Socket($ip, $port); + }</strong> +} +?> +</pre> + Il s'agit là de la seule modification dans le code de l'application. + </p> + <p> + Pour le scénario de test, nous devons créer + une sous-classe de manière à intercepter la création de la socket... +<pre> +<strong>class TelnetTestVersion extends Telnet { + var $_mock; + + function TelnetTestVersion(&$mock) { + $this->_mock = &$mock; + $this->Telnet(); + } + + function &_createSocket() { + return $this->_mock; + } +}</strong> +</pre> + Ici j'ai déplacé la fantaisie dans le constructeur, + mais un setter aurait fonctionné tout aussi bien. + Notez bien que la fantaisie est placée dans une variable + d'objet avant que le constructeur ne soit attaché. + C'est nécessaire dans le cas où le constructeur appelle + <span class="new_code">connect()</span>. + Autrement il pourrait donner un valeur nulle à partir de + <span class="new_code">_createSocket()</span>. + </p> + <p> + Après la réalisation de tout ce travail supplémentaire + le scénario de test est assez simple. + Nous avons juste besoin de tester notre nouvelle classe à la place... +<pre> +class TelnetTest extends UnitTestCase { + ... + function testConnection() {<strong> + $socket = &new MockSocket($this); + ... + $telnet = &new TelnetTestVersion($socket); + $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); + ...</strong> + } +} +</pre> + Cette nouvelle classe est très simple bien sûr. + Elle ne fait qu'initier une valeur renvoyée, à la manière + d'une fantaisie. Ce serait pas mal non plus si elle pouvait + vérifier les paramètres entrants. + Exactement comme un objet fantaisie. + Il se pourrait bien que nous ayons à réaliser cette astuce régulièrement : + serait-il possible d'automatiser la création de cette sous-classe ? + </p> + + <p><a class="target" name="partiel"><h2>Un objet fantaisie partiel</h2></a></p> + <p> + Bien sûr la réponse est "oui" + ou alors j'aurais arrêté d'écrire depuis quelques temps déjà ! + Le test précédent a représenté beaucoup de travail, + mais nous pouvons générer la sous-classe en utilisant + une approche à celle des objets fantaisie. + </p> + <p> + Voici donc une version avec objet fantaisie partiel du test... +<pre> +<strong>Mock::generatePartial( + 'Telnet', + 'TelnetTestVersion', + array('_createSocket'));</strong> + +class TelnetTest extends UnitTestCase { + ... + function testConnection() {<strong> + $socket = &new MockSocket($this); + ... + $telnet = &new TelnetTestVersion($this); + $telnet->setReturnReference('_createSocket', $socket); + $telnet->Telnet(); + $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); + ...</strong> + } +} +</pre> + La fantaisie partielle est une sous-classe de l'original + dont on aurait "remplacé" les méthodes sélectionnées + avec des versions de test. L'appel à <span class="new_code">generatePartial()</span> + nécessite trois paramètres : la classe à sous classer, + le nom de la nouvelle classe et une liste des méthodes à simuler. + </p> + <p> + Instancier les objets qui en résultent est plutôt délicat. + L'unique paramètre du constructeur d'un objet fantaisie partiel + est la référence du testeur unitaire. + Comme avec les objets fantaisie classiques c'est nécessaire + pour l'envoi des résultats de test en réponse à la vérification des attentes. + </p> + <p> + Une nouvelle fois le constructeur original n'est pas lancé. + Indispensable dans le cas où le constructeur aurait besoin + des méthodes fantaisie : elles n'ont pas encore été initiées ! + Nous initions les valeurs retournées à cet instant et + ensuite lançons le constructeur avec ses paramètres normaux. + Cette construction en trois étapes de "new", + suivie par la mise en place des méthodes et ensuite + par la lancement du constructeur proprement dit est + ce qui distingue le code d'un objet fantaisie partiel. + </p> + <p> + A part pour leur construction, toutes ces méthodes + fantaisie ont les mêmes fonctionnalités que dans + le cas des objets fantaisie et toutes les méthodes + non fantaisie se comportent comme avant. + Nous pouvons mettre en place des attentes très facilement... +<pre> +class TelnetTest extends UnitTestCase { + ... + function testConnection() { + $socket = &new MockSocket($this); + ... + $telnet = &new TelnetTestVersion($this); + $telnet->setReturnReference('_createSocket', $socket);<strong> + $telnet->expectOnce('_createSocket', array('127.0.0.1', 21));</strong> + $telnet->Telnet(); + $telnet->connect('127.0.0.1', 21, 'Me', 'Secret'); + ...<strong> + $telnet->tally();</strong> + } +} +</pre> + </p> + + <p><a class="target" name="moins"><h2>Tester moins qu'une classe</h2></a></p> + <p> + Les méthodes issues d'un objet fantaisie n'ont pas + besoin d'être des méthodes fabrique, Il peut s'agir + de n'importe quelle sorte de méthode. + Ainsi les objets fantaisie partiels nous permettent + de prendre le contrôle de n'importe quelle partie d'une classe, + le constructeur excepté. Nous pourrions même aller jusqu'à + créer des fantaisies sur toutes les méthodes à part celle + que nous voulons effectivement tester. + </p> + <p> + Cette situation est assez hypothétique, étant donné + que je ne l'ai jamais essayée. Je suis ouvert à cette possibilité, + mais je crains qu'en forçant la granularité d'un objet + on n'obtienne pas forcément un code de meilleur qualité. + Personnellement j'utilise les objets fantaisie partiels + comme moyen de passer outre la création ou alors + de temps en temps pour tester le modèle de conception TemplateMethod. + </p> + <p> + Pour choisir le mécanisme à utiliser, on en revient + toujours aux standards de code de votre projet. + </p> + + </div> + References and related information... + <ul> +<li> + La page du projet SimpleTest sur + <a href="http://sourceforge.net/projects/simpletest/">SourceForge</a>. + </li> +<li> + <a href="http://simpletest.org/api/">L'API complète pour SimpleTest</a> + à partir de PHPDoc. + </li> +<li> + La méthode fabrique protégée est décrite dans + <a href="http://www-106.ibm.com/developerworks/java/library/j-mocktest.html"> + cet article d'IBM</a>. Il s'agit de l'unique papier + formel que j'ai vu sur ce problème. + </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> |