PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/source/it/mock_objects_tutorial.xml

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