PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/docs/source/fr/mock_objects_tutorial.xml

https://bitbucket.org/bpanulla/simpletest
XML | 375 lines | 356 code | 18 blank | 1 comment | 0 complexity | 8d8627dfa680617cc87079028bda97de MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?xml version="1.0" encoding="ISO-8859-1" ?>
  2. <!-- $Id: mock_objects_tutorial.xml 2027 2011-06-08 20:20:43Z pp11 $ -->
  3. <page title="Objets fantaisie" here="Utiliser des objets fantaisie">
  4. <synchronisation lang="en" version="1979" date="08/06/2011" maintainer="pp11" />
  5. <long_title>tutorial sur les tests unitaires en PHP - Utiliser les objets fantaisie en PHP</long_title>
  6. <content>
  7. <section name="remaniement" title="Remanier les tests à nouveau">
  8. <p>
  9. Avant d'ajouter de nouvelles fonctionnalités
  10. il y a du remaniement à faire.
  11. Nous allons effectuer des tests chronométrés
  12. et la classe <code>TimeTestCase</code> a définitivement
  13. besoin d'un fichier propre.
  14. Appelons le <em>tests/time_test_case.php</em>...
  15. <php><![CDATA[
  16. <strong><?php
  17. require_once('simpletest/unit_tester.php');
  18. abstract class TimeTestCase extends UnitTestCase {
  19. function assertSameTime($time1, $time2, $message = '') {
  20. if (! $message) {
  21. $message = "Time [$time1] should match time [$time2]";
  22. }
  23. $this->assertTrue(
  24. ($time1 == $time2) || ($time1 + 1 == $time2),
  25. $message);
  26. }
  27. }
  28. ?></strong>
  29. ]]></php>
  30. Nous pouvons lors utiliser <code>require()</code>
  31. pour incorporer ce fichier dans le script <em>all_tests.php</em>.
  32. </p>
  33. </section>
  34. <section name="timestamp" title="Ajouter un timestamp au Log">
  35. <p>
  36. Je ne sais pas trop quel devrait être
  37. le format du message de log pour le test alors
  38. pour vérifier le timestamp nous pourrions juste
  39. faire la plus simple des choses possibles,
  40. c'est à dire rechercher une suite de chiffres.
  41. <php><![CDATA[
  42. <?php
  43. require_once('simpletest/autorun.php');<strong>
  44. require_once('time_test_case.php');</strong>
  45. require_once('../classes/log.php');<strong>
  46. require_once('../classes/clock.php');
  47. class TestOfLogging extends TimeTestCase {</strong>
  48. function setUp() {
  49. @unlink('../temp/test.log');
  50. }
  51. function tearDown() {
  52. @unlink('../temp/test.log');
  53. }
  54. function getFileLine($filename, $index) {
  55. $messages = file($filename);
  56. return $messages[$index];
  57. }
  58. function testCreatingNewFile() { ... }
  59. function testAppendingToFile() { ... }
  60. <strong>
  61. function testTimestampIsEmittedInMessage() {
  62. $log = new Log('../temp/test.log');
  63. $log->message('Test line');
  64. $this->assertTrue(
  65. preg_match('/(\d+)/', $this->getFileLine('../temp/test.log', 0), $matches),
  66. 'Found timestamp');
  67. $clock = new clock();
  68. $this->assertSameTime((integer)$matches[1], $clock->now(), 'Correct time');
  69. }</strong>
  70. }
  71. ?>
  72. ]]></php>
  73. Ce scénario de test crée un nouvel objet <code>Log</code>
  74. et écrit un message. Nous recherchons une suite de chiffres
  75. et nous la comparons à l'horloge présente en utilisant
  76. notre objet <code>Clock</code>. Bien sûr ça ne marche pas
  77. avant d'avoir écrit le code.
  78. <div class="demo">
  79. <h1>All tests</h1>
  80. <span class="pass">Pass</span>: log_test.php->Log class test->testAppendingToFile->Expecting [/Test line 1/] in [Test line 1]<br />
  81. <span class="pass">Pass</span>: log_test.php->Log class test->testAppendingToFile->Expecting [/Test line 2/] in [Test line 2]<br />
  82. <span class="pass">Pass</span>: log_test.php->Log class test->testCreatingNewFile->Created before message<br />
  83. <span class="pass">Pass</span>: log_test.php->Log class test->testCreatingNewFile->File created<br />
  84. <span class="fail">Fail</span>: log_test.php->Log class test->testTimestampIsEmittedInMessage->Found timestamp<br />
  85. <br />
  86. <b>Notice</b>: Undefined offset: 1 in <b>/home/marcus/projects/lastcraft/tutorial_tests/tests/log_test.php</b> on line <b>44</b><br />
  87. <span class="fail">Fail</span>: log_test.php->Log class test->testTimestampIsEmittedInMessage->Correct time<br />
  88. <span class="pass">Pass</span>: clock_test.php->Clock class test->testClockAdvance->Advancement<br />
  89. <span class="pass">Pass</span>: clock_test.php->Clock class test->testClockTellsTime->Now is the right time<br />
  90. <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">3/3 test cases complete.
  91. <strong>6</strong> passes, <strong>2</strong> fails and <strong>2</strong> exceptions.</div>
  92. </div>
  93. Cette suite de tests montre encore les succès
  94. de notre modification précédente.
  95. </p>
  96. <p>
  97. Nous pouvons faire passer les tests en ajoutant
  98. simplement un timestamp à l'écriture dans le fichier.
  99. Oui, bien sûr, tout ceci est assez trivial et d'habitude
  100. je ne le testerais pas aussi fanatiquement,
  101. mais ça va illustrer un problème plus général...
  102. Le fichier <em>log.php</em> devient...
  103. <php><![CDATA[
  104. <?php<strong>
  105. require_once('../classes/clock.php');</strong>
  106. class Log {
  107. private $path;
  108. function __construct($path) {
  109. $this->path = $path;
  110. }
  111. function message($message) {
  112. <strong>$clock = new Clock();</strong>
  113. $file = fopen($this->path, 'a');
  114. <strong>fwrite($file, "[" . $clock->now() . "] $message\n");</strong>
  115. fclose($file);
  116. }
  117. }
  118. ?>
  119. ]]></php>
  120. Les tests devraient passer.
  121. </p>
  122. <p>
  123. Par contre notre nouveau test est plein de problèmes.
  124. Qu'est-ce qui se passe si notre format de temps change ?
  125. Les choses vont devenir largement plus compliquées
  126. si ça venait à se produire.
  127. Cela veut aussi dire que n'importe quel changement
  128. du format de notre classe horloge causera aussi
  129. un échec dans les tests de log.
  130. Bilan : nos tests de log sont tout mélangés
  131. avec les test d'horloge et par la même très fragiles.
  132. Tester à la fois des facettes de l'horloge
  133. et d'autres du log manque de cohésion,
  134. ou de focalisation étanche si vous préférez.
  135. </p>
  136. <p>
  137. Nos problèmes sont causés en partie parce que
  138. le résultat de l'horloge est imprévisible alors que
  139. l'unique chose à tester est la présence
  140. du résultat de <code>Clock::now()</code>.
  141. Peu importe le contenu de l'appel de cette méthode.
  142. </p>
  143. <p>
  144. Pouvons-nous rendre cet appel prévisible ?
  145. Oui si nous pouvons forcer le loggueur à utiliser
  146. une version factice de l'horloge lors du test.
  147. Cette classe d'horloge factice devrait se comporter
  148. exactement comme la classe <code>Clock</code>
  149. à part une sortie fixée dans la méthode <code>now()</code>.
  150. Et au passage, ça nous affranchirait même
  151. de la classe <code>TimeTestCase</code> !
  152. </p>
  153. <p>
  154. Nous pourrions écrire une telle classe assez
  155. facilement même s'il s'agit d'un boulot plutôt fastidieux.
  156. Nous devons juste créer une autre classe
  157. d'horloge avec la même interface sauf que
  158. la méthode <code>now()</code> retourne une valeur modifiable
  159. via une autre méthode d'initialisation.
  160. C'est plutôt pas mal de travail pour un test plutôt mineur.
  161. </p>
  162. <p>
  163. Sauf que ça se fait presque sans effort.
  164. </p>
  165. </section>
  166. <section name="fantaisie" title="Une horloge fantaisie">
  167. <p>
  168. Pour atteindre le nirvana de l'horloge instantané
  169. pour test nous n'avons besoin que de trois lignes de code supplémentaires...
  170. <php><![CDATA[
  171. require_once('simpletest/mock_objects.php');
  172. ]]></php>
  173. Cette instruction inclut le code de générateur
  174. d'objet fantaisie. Le plus simple reste de le mettre
  175. dans le script <em>all_tests.php</em> étant donné
  176. qu'il est utilisé assez fréquemment.
  177. <php><![CDATA[
  178. Mock::generate('Clock');
  179. ]]></php>
  180. C'est la ligne qui fait le travail.
  181. Le générateur de code scanne la classe,
  182. en extrait toutes ses méthodes, crée le code
  183. pour générer une classe d'héritage ou avec une interface identique,
  184. mais en ajoutant le nom &quot;Mock&quot;
  185. et ensuite <code>eval()</code> le nouveau code pour créer la nouvelle classe.
  186. <php><![CDATA[
  187. $clock = new MockClock();
  188. ]]></php>
  189. Cette ligne peut être ajoutée dans n'importe
  190. quelle méthode de test qui nous intéresserait.
  191. Elle crée l'horloge fantaisie prête à recevoir nos instructions.
  192. </p>
  193. <p>
  194. Notre scénario de test en est à ses premiers pas
  195. vers un nettoyage radical.
  196. Le premier est d'utiliser effectivement
  197. le <code>MockClock</code> dans notre test...
  198. <php><![CDATA[
  199. <?php
  200. require_once('simpletest/autorun.php');<strong>
  201. require_once('simpletest/mock_objects.php');</strong>
  202. require_once('../classes/log.php');
  203. require_once('../classes/clock.php');<strong>
  204. Mock::generate('Clock');</strong>
  205. class TestOfLogging extends <strong>UnitTestCase</strong> {
  206. function setUp() {
  207. @unlink('../temp/test.log');
  208. }
  209. function tearDown() {
  210. @unlink('../temp/test.log');
  211. }
  212. function getFileLine($filename, $index) {
  213. $messages = file($filename);
  214. return $messages[$index];
  215. }
  216. function testCreatingNewFile() { ... }
  217. function testAppendingToFile() { ... }
  218. function testTimestamps() {
  219. <strong>$clock = new MockClock();</strong>
  220. <strong>$clock->returns('now', 'Timestamp');</strong>
  221. $log = new Log('../temp/test.log');
  222. $log->message('Test line', $clock);
  223. $this->assertPattern(
  224. <strong>'/Timestamp/'</strong>,
  225. $this->getFileLine('../temp/test.log', 0));
  226. }
  227. }
  228. ?>
  229. ]]></php>
  230. Notez que nous n'avons plus besoin de notre <code>TimeTestCase</code>.
  231. Il sera encore utile pour l'objet <code>Clock</code>, mais
  232. nous n'en n'avons plus besoin pour le <code>Log</code>.
  233. </p>
  234. <p>
  235. Cette méthode de test crée un objet <code>MockClock</code>
  236. puis définit la valeur retourné par la méthode
  237. <code>now()</code> par la chaîne &quot;Timestamp&quot;.
  238. A chaque fois que nous appelons <code>$clock->now()</code>,
  239. elle retournera cette même chaîne.
  240. Ça devrait être quelque chose de facilement repérable.
  241. </p>
  242. <p>
  243. Ensuite nous créons notre loggueur et envoyons un message.
  244. Nous incluons dans l'appel <code>message()</code>
  245. l'horloge que nous souhaitons utiliser.
  246. Ça veut dire que nous aurons à ajouter un paramètre
  247. optionnel à la classe de log pour rendre ce test possible...
  248. <php><![CDATA[
  249. class Log {
  250. private $path;
  251. function Log($path) {
  252. $this->path = $path;
  253. }
  254. function message($message, <strong>$clock = false</strong>) {<strong>
  255. $clock = $clock? $clock : new Clock();</strong>
  256. $file = fopen($this->path, 'a');
  257. fwrite($file, "[" . $clock->now() . "] $message\n");
  258. fclose($file);
  259. }
  260. }
  261. ]]></php>
  262. Maintenant tous les tests passent et ils ne testent
  263. que le code du loggueur. Nous pouvons à nouveau respirer.
  264. </p>
  265. <p>
  266. Est-ce que ce paramètre supplémentaire dans la classe <code>Log</code>
  267. vous gêne ? Nous n'avons changé l'interface que
  268. pour faciliter les tests après tout.
  269. Les interfaces ne sont-elles pas la chose la plus importante ?
  270. Avons nous souillé notre classe avec du code de test ?
  271. </p>
  272. <p>
  273. Peut-être, mais réfléchissez à ce qui suit.
  274. A la prochaine occasion,
  275. regardez une carte avec des circuits imprimés,
  276. peut-être la carte mère de l'ordinateur que
  277. ous regardez actuellement. Sur la plupart d'entre elles
  278. vous trouverez un trou bizarre et vide
  279. ou alors un point de soudure sans rien de fixé
  280. ou même une épingle ou une prise sans aucune fonction évidente.
  281. Peut-être certains sont en prévision d'une expansion
  282. ou d'une variation future, mais la plupart n'y sont que pour les tests.
  283. </p>
  284. <p>
  285. Les usines qui fabriquent ces cartes imprimées
  286. par centaine de milliers gaspillent des matières premières
  287. sur des pièces qui n'ajoutent rien à la fonction finale.
  288. Si les ingénieurs matériel peuvent faire quelques sacrifices
  289. à l'élégance, je suis sûr que nous pouvons aussi le faire.
  290. Notre sacrifice ne gaspille pas de matériel après tout.
  291. </p>
  292. <p>
  293. Ça vous gêne encore ? En fait moi aussi.
  294. Si ça vous gêne vraiment alors déplacez la création
  295. de l'horloge dans une autre méthode mère protégée.
  296. Ensuite sous classez l'horloge pour le test
  297. et écrasez la méthode mère avec une qui renvoie le leurre.
  298. Vos tests sont bancals mais votre interface est intacte.
  299. </p>
  300. <p>
  301. Je vous laisse la décision finale, mais veuillez tenir compte
  302. que nous verrons des techniques d'automatisation dans
  303. la prochaine section afin de rendre ce sous-classage
  304. plus facile.
  305. </p>
  306. </section>
  307. </content>
  308. <internal>
  309. <link>
  310. <a href="#remaniement">Remanier les tests</a>
  311. dans le but de réutiliser notre nouveau test de temps.
  312. </link>
  313. <link>
  314. Ajouter des <a href="#timestamp">timestamps de Log</a>.
  315. </link>
  316. <link>
  317. <a href="#fantaisie">Créer une horloge fantaisie</a>
  318. pour rendre les tests cohésifs.
  319. </link>
  320. </internal>
  321. <external>
  322. <link>
  323. La section précédente :
  324. <a href="first_test_tutorial.php">tutorial de test unitaire</a>.
  325. </link>
  326. <link>
  327. La section suivante :
  328. <a href="boundary_classes_tutorial.php">les frontières de l'application</a>.
  329. </link>
  330. <link>
  331. Vous aurez besoin du
  332. <a href="simple_test.php">framework de test SimpleTest</a>
  333. pour essayer ces exemples.
  334. </link>
  335. <link>
  336. Documents sur les <a href="http://www.mockobjects.com/">objets fantaisie</a>.
  337. </link>
  338. </external>
  339. <meta>
  340. <keywords>
  341. développement logiciel,
  342. programmation php,
  343. outils de développement logiciel,
  344. tutoriel php,
  345. scripts php gratuits,
  346. architecture,
  347. ressources php,
  348. objet fantaisie,
  349. junit,
  350. phpunit,
  351. simpletest,
  352. test php,
  353. outil de test unitaire,
  354. suite de test php
  355. </keywords>
  356. </meta>
  357. </page>