PageRenderTime 69ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/trunk/docs/source/it/mock_objects_tutorial.xml

https://bitbucket.org/bpanulla/simpletest
XML | 352 lines | 333 code | 18 blank | 1 comment | 0 complexity | 4194f0fbdf7c4d92a755666edd0f32d9 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?xml version="1.0"?>
  2. <!-- $Id: mock_objects_tutorial.xml 2034 2011-06-08 21:51:35Z pp11 $ -->
  3. <page title="Gli oggetti mock" here="Tutorial: gli oggetti mock">
  4. <synchronisation lang="en" version="0" date="08/06/2011" maintainer="arialdomartini" />
  5. <long_title>PHP unit testing tutorial - Using mock objects in PHP</long_title>
  6. <content>
  7. <section name="refactor" title="Refactoring the tests again">
  8. <p>
  9. Prima di aggiungere nuove funzioanlità c'è del refactoring da fare.
  10. Abbiamo da eseguire dei test riguardanti il tempo e
  11. la classe <code>TimeTestCase</code> ha definitivamente
  12. bisogno di un suo proprio file.
  13. Chiamiamolo <em>tests/time_test_case.php</em>:
  14. <php><![CDATA[
  15. <strong><?php
  16. require_once('simpletest/unit_tester.php');
  17. abstract class TimeTestCase extends UnitTestCase {
  18. function assertSameTime($time1, $time2, $message = '') {
  19. if (! $message) {
  20. $message = "Time [$time1] should match time [$time2]";
  21. }
  22. $this->assertTrue(
  23. ($time1 == $time2) || ($time1 + 1 == $time2),
  24. $message);
  25. }
  26. }
  27. ?></strong>
  28. ]]></php>
  29. Potremmo includerlo con <code>require()</code> nello script
  30. <em>all_tests.php</em>.
  31. </p>
  32. </section>
  33. <section name="timestamp" title="Aggiungere un timestamp al log">
  34. <p>
  35. Siccome non sappiamo con esattezza quale sia il formato del messaggio
  36. di log durante il test, verificheremo il più semplice caso, quello di una
  37. sequenza di numeri.
  38. <php><![CDATA[
  39. <?php
  40. require_once('simpletest/autorun.php');<strong>
  41. require_once('time_test_case.php');</strong>
  42. require_once('../classes/log.php');<strong>
  43. require_once('../classes/clock.php');
  44. class TestOfLogging extends TimeTestCase {</strong>
  45. function setUp() {
  46. @unlink('../temp/test.log');
  47. }
  48. function tearDown() {
  49. @unlink('../temp/test.log');
  50. }
  51. function getFileLine($filename, $index) {
  52. $messages = file($filename);
  53. return $messages[$index];
  54. }
  55. function testCreatingNewFile() { ... }
  56. function testAppendingToFile() { ... }
  57. <strong>
  58. function testTimestampIsEmittedInMessage() {
  59. $log = new Log('../temp/test.log');
  60. $log->message('Test line');
  61. $this->assertTrue(
  62. preg_match('/(\d+)/', $this->getFileLine('../temp/test.log', 0), $matches),
  63. 'Found timestamp');
  64. $clock = new clock();
  65. $this->assertSameTime((integer)$matches[1], $clock->now(), 'Correct time');
  66. }</strong>
  67. }
  68. ?>
  69. ]]></php>
  70. Il test case genera un nuovo oggetto <code>Log</code> e scrive un messaggio.
  71. Cerchiamo una sequenza di numeri e la confrontiamo con l'ora corrente
  72. utilizzando l'oggetto <code>Clock</code>.
  73. Naturalmente, niente funzionerà prima che si scriva il codice.
  74. <div class="demo">
  75. <h1>All tests</h1>
  76. <span class="pass">Pass</span>: log_test.php->Log class test->testappendingtofile->Expecting [/Test line 1/] in [Test line 1]<br />
  77. <span class="pass">Pass</span>: log_test.php->Log class test->testappendingtofile->Expecting [/Test line 2/] in [Test line 2]<br />
  78. <span class="pass">Pass</span>: log_test.php->Log class test->testcreatingnewfile->Created before message<br />
  79. <span class="pass">Pass</span>: log_test.php->Log class test->testcreatingnewfile->File created<br />
  80. <span class="fail">Fail</span>: log_test.php->Log class test->testtimestamps->Found timestamp<br />
  81. <br />
  82. <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 />
  83. <span class="fail">Fail</span>: log_test.php->Log class test->testtimestamps->Correct time<br />
  84. <span class="pass">Pass</span>: clock_test.php->Clock class test->testclockadvance->Advancement<br />
  85. <span class="pass">Pass</span>: clock_test.php->Clock class test->testclocktellstime->Now is the right time<br />
  86. <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">3/3 test cases complete.
  87. <strong>6</strong> passes, <strong>2</strong> fails and <strong>2</strong> exceptions.</div>
  88. </div>
  89. La test suite continua a mostrare i successi della nostra prima modifica.
  90. </p>
  91. <p>
  92. Possiamo fare in modo che i test passino semplicemente aggiungendo un
  93. timestemp al momento della scrittura del file.
  94. , naturalmente tutto questo è insignificante e
  95. normalmente non collaudo a questi livelli da fanatismo, ma l'esempio
  96. è buono per illustrare un problema più generale.
  97. Il file <em>log.php</em> diventa:
  98. <php><![CDATA[
  99. <?php<strong>
  100. require_once('../classes/clock.php');</strong>
  101. class Log {
  102. private $path;
  103. function __construct($path) {
  104. $this->path = $path;
  105. }
  106. function message($message) {<strong>
  107. $clock = new Clock();</strong>
  108. $file = fopen($this->path, 'a');<strong>
  109. fwrite($file, "[" . $clock->now() . "] $message\n");</strong>
  110. fclose($file);
  111. }
  112. }
  113. ?>
  114. ]]></php>
  115. Il test dovrebbe passare.
  116. </p>
  117. <p>
  118. Il nostro nuovo test, tuttavia, è pieno di problemi.
  119. Cosa accadrebbe se il formato di stampa dell'ora dovesse
  120. cambiare?
  121. Le cose diventerebbero un bel po' più complicate se questo accadesse.
  122. Comportebbe anche modificare il formato delle ore della classe
  123. clock, il che porterebbe altri test a fallire.
  124. In altre parole, i test di log sono accoppiati con i test di
  125. clock e sono estremamente fragili.
  126. Mancano di coesione, il che equivale a dire che non sono
  127. molto a fuoco su un solo obiettivo, dal momento che collaudano
  128. anche aspetti del clock oltre a quelli del log.
  129. I nostri problemi sono in parte causati dal fatto che l'output
  130. di clock è imprevedibile, anche se quel che si intendeva realmente
  131. fare era collaudare che il messaggio del log contenesse l'output di
  132. <code>Clock::now()</code>, qualunque esso fosse.
  133. Non ci interessa davvero il contenuto di quella invocazione di metodo.
  134. </p>
  135. <p>
  136. Dal punto di vista del il collaudo, tutto quel che è stato fatto
  137. fin'ora è errato.
  138. </p>
  139. <p>
  140. Possiamo rendere la chiamata predicibile?
  141. Potremmo se solo potessi indurre log ad usare una versione
  142. fasulla di clock almeno per la durata del test.
  143. La falsa classe clock dovrebbe comportarsi come la classe
  144. <code>Clock</code> apparte per l'output fisso del metodo
  145. <code>now()</code>.
  146. Hey, questo ci libererebbe perfino dall'uso della classe
  147. <code>TimeTestCase</code>!
  148. </p>
  149. <p>
  150. Potremmo scriverefacilmente una classe di questo tipo anche
  151. se il lavoro risulterebbe noioso.
  152. Creeremmo un'altra classe clock con la stessa interfaccia
  153. ma con il metodo <code>now()</code> pensato per restituire
  154. un valore che potremmo essere in grado di modificare attraverso
  155. degli appositi metodi setter.
  156. E' un bel lavoro, per un test di minore entità.
  157. </p>
  158. <p>
  159. Tranne il fatto che, nella realtà, non c'è affatto alcuno
  160. sforzo da fare.
  161. </p>
  162. </section>
  163. <section name="mock" title="A mock clock">
  164. <p>
  165. Per raggiungere istantaneamente il nirvana del clock per i test
  166. abbiamo bisogno solo di tre righe di codice:
  167. <php><![CDATA[
  168. require_once('simpletest/mock_objects.php');
  169. ]]></php>
  170. Questa include il codice del generatore di mock.
  171. La cosa più semplice è mettere questa riga in <em>all_tests.php</em>
  172. dal momento che verrà usata spesso.
  173. <php><![CDATA[
  174. Mock::generate('Clock');
  175. ]]></php>
  176. Questa è la linea che fa il vero lavoro.
  177. Il generatore di codice scansiona tutti i metodi della classe ,
  178. crea il codice per generare un'interfaccia identica o una classe
  179. ereditata ed aggiunge il prefisso &quot;Mock&quot; al nome.
  180. Dopo di che, esegue con <code>eval()</code> il codice per
  181. creare la nuova classe.
  182. <php><![CDATA[
  183. $clock = new MockClock($this);
  184. ]]></php>
  185. Questa linea può essere aggiunta a qualsiasi metodo di test.
  186. Genera un oggetto clock fasullo pronto per ricevere istruzioni.
  187. </p>
  188. <p>
  189. Il nostro test è il primo passo per una radicale pulizia:
  190. <php><![CDATA[
  191. <?php
  192. require_once('simpletest/autorun.php');<strong>
  193. require_once('simpletest/mock_objects.php');</strong>
  194. require_once('../classes/log.php');
  195. require_once('../classes/clock.php');<strong>
  196. Mock::generate('Clock');</strong>
  197. class TestOfLogging extends <strong>UnitTestCase</strong> {
  198. function setUp() {
  199. @unlink('../temp/test.log');
  200. }
  201. function tearDown() {
  202. @unlink('../temp/test.log');
  203. }
  204. function getFileLine($filename, $index) {
  205. $messages = file($filename);
  206. return $messages[$index];
  207. }
  208. function testCreatingNewFile() { ... }
  209. function testAppendingToFile() { ... }
  210. function testTimestamps() {<strong>
  211. $clock = new MockClock($this);
  212. $clock->returns('now', 'Timestamp');
  213. $log = new Log('../temp/test.log');
  214. $log->message('Test line', &$clock);
  215. $this->assertPattern(
  216. '/Timestamp/',
  217. $this->getFileLine('../temp/test.log', 0),
  218. 'Found timestamp');</strong>
  219. }
  220. }
  221. ?>
  222. ]]></php>
  223. Questo metodo di test genera un oggetto <code>MockClock</code>
  224. ed imposta valore di ritorno del metodo <code>now()</code> perché
  225. sia la stringa
  226. &quot;Timestamp&quot;.
  227. Ogni volta che <code>$clock->now()</code> viene invocato
  228. restituirà quella stringa.
  229. Questo dovrebbe essere facile da comprendere.
  230. </p>
  231. <p>
  232. Successivamente, creiamo log e gli inviamo un messaggio.
  233. Passiamo a <code>message()</code> il clock
  234. che desideriamo usare.
  235. Questo comporta che per rendere possibile il collaudo ci sia
  236. la necessità di un parametro aggiuntivo alla classe di log:
  237. <php><![CDATA[
  238. class Log {
  239. private $path;
  240. function Log($path) {
  241. $this->path = $path;
  242. }
  243. function message($message, <strong>$clock = false</strong>) {<strong>
  244. $clock = $clock? $clock : new Clock();</strong>
  245. $file = fopen($this->path, 'a');
  246. fwrite($file, "[" . $clock->now() . "] $message\n");
  247. fclose($file);
  248. }
  249. }
  250. ]]></php>
  251. Adesso tutti i test passano e collaudano solo il codice del log.
  252. Possiamo tirare nuovamente un sospiro di sollievo.
  253. </p>
  254. <p>
  255. Ti disturba il parametro extra in <code>Log</code>?
  256. Dopo tutto, abbiamo modificato l'interfaccia al solo scopo di facilitare il
  257. collaudo.
  258. Non erano le interfaccie le cose più importanti?
  259. Non abbiamo sporcato la nostra classe con il codice di test?
  260. </p>
  261. <p>
  262. Senza dubbio non è il caso ideale ma considera questo:
  263. alla prossima occasione che ti si presentam dai un occhio ad un
  264. circuito stampato, magari quello della scheda madre del computer dal
  265. quale stai leggendo proprio adesso.
  266. Nella maggior parte delle schede troverai degli strani fori o dei punti
  267. di saldatura inutilizzati o forse dei pin o dei socket che non
  268. sembrano avere una funzione certa.
  269. C'è la possibilità che alcune di queste servano per
  270. l'espansione e la modifica della scheda ma la maggior parte
  271. è per i collaudi.
  272. </p>
  273. <p>
  274. Le fabbriche che producono le schede in migliaia di esemplari consumano
  275. materiale su parti che non aggiungono alcuna funzione al prodotto finale.
  276. Se gli ingegneri elettronici possono fare questo sacrificio di eleganza
  277. sono certo che possiamo farlo anche noi.
  278. Del resto, il nostro sacrificio non spreca nemmeno materiale.
  279. </p>
  280. <p>
  281. Ti disturba lo stesso?
  282. A dire la verità disturba anche me.
  283. Se la cosa davvero ti fastidio, allora sposta la creazione dell'orologio
  284. dentro un apposito metodo di factoring protetto.
  285. Dopo di che, estendi la classe clock per i collaudi e
  286. fai l'overriding del metodo factory con uno che restituisca il mock.
  287. I test risulteranno più scomodi ma l'interfaccia resterà intatta.
  288. </p>
  289. <p>
  290. La decisione è personale ma nota che nella prossima sezione
  291. si vedranno alcuni automatismi per facilitare il subclassing.
  292. </p>
  293. </section>
  294. </content>
  295. <internal>
  296. <link>
  297. <a href="#refactor">Refactoring dei test</a> per il riutilizzo dei test.
  298. </link>
  299. <link><a href="#timestamp">Aggiungere un timestamp ai log</a>.</link>
  300. <link><a href="#mock">Mock dell'orologio</a> per rendre il test più coeso.</link>
  301. </internal>
  302. <external>
  303. <link>
  304. This follows the <a href="first_test_tutorial.php">unit test tutorial</a>.
  305. </link>
  306. <link>
  307. Next is distilling <a href="boundary_classes_tutorial.php">boundary classes</a>.
  308. </link>
  309. <link>
  310. You will need the <a href="simple_test.php">SimpleTest</a>
  311. tool to run the examples.
  312. </link>
  313. <link>
  314. <a href="http://www.mockobjects.com/">Mock objects</a> papers.
  315. </link>
  316. </external>
  317. <meta>
  318. <keywords>
  319. software development,
  320. php programming,
  321. programming php,
  322. software development tools,
  323. php tutorial,
  324. free php scripts,
  325. architecture,
  326. php resources,
  327. mock objects,
  328. junit,
  329. php testing,
  330. unit test,
  331. php testing
  332. </keywords>
  333. </meta>
  334. </page>