PageRenderTime 141ms CodeModel.GetById 99ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/lastcraft/simpletest/docs/source/it/first_test_tutorial.xml

https://bitbucket.org/mjmeintjes/mattsum-datafeed-helpers
XML | 448 lines | 434 code | 13 blank | 1 comment | 0 complexity | f972dc2ac2fdb69cffad2149ab7034ac MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?xml version="1.0"?>
  2. <!-- $Id$ -->
  3. <page title="Tutorial: unit testing con SimpleTest" here="Tutorial: unit testing con SimpleTest">
  4. <synchronisation lang="en" version="0" date="08/06/2011" maintainer="arialdomartini" />
  5. <long_title>PHP unit testing tutorial - Creating an example test case in PHP</long_title>
  6. <content>
  7. <introduction>
  8. <p>
  9. Nel caso tu sia inesperto di unit testing, la raccomandazione è
  10. di provare di volta in volta il codice che si incontrerà durante lettura del tutorial:
  11. non richiede molto da scrivere e fornisce la sensazione di quale sia il ritmo
  12. della programmazione guidata dal collaudo.
  13. </p>
  14. <p>
  15. Per eseguire gli esempi è necessaria una directory vuota contenente
  16. le directory <em>classes</em>, <em>tests</em> e <em>temp</em>.
  17. Decomprimere quindi il framework <a href="download.php">SimpleTest</a>
  18. in <em>tests</em> ed assicurarsi che il web server possa raggiungere
  19. le varie posizioni.
  20. </p>
  21. </introduction>
  22. <section name="new" title="Un nuovo test case">
  23. <p>
  24. Nel <a href="start-testing.html">tutorial rapido</a>
  25. è stato mostrato il collaudo di unità applicato ad una semplice classe di log.
  26. In questo tutorial su SimpleTest ho intenzione di provare
  27. a raccontare l'argomento dello sviluppo della classe nella sua interezza.
  28. La classe dell'esempio è piccola e semplice e nel corso dell'introduzione
  29. riceverà probabilmente più attenzione di quanta ne riceverebbe
  30. realmente in produzione.
  31. Ma perfino una classe minuscola come questa richiederà decisioni sul
  32. design di soprendente difficoltà.
  33. </p>
  34. <p>
  35. Può darsi decisioni fin troppo difficili?
  36. Piuttosto che tentare di disegnare il tutto fin dall'inizio,
  37. si partirà con un requisito noto: il fatto che si vuole
  38. scrivere un messaggio su un file.
  39. Il messaggio deve essere accodato al file se questo esiste.
  40. Successivamente ci si porrà ilproblema di gestire le
  41. priorità, i filtraggi etc, ma per adesso posizioneremo
  42. il requisito della scrittura del file in cima ai nostri pensieri.
  43. Per paura di confonderci non penseremo a nient'altro.
  44. Ok, scriviamo un test:
  45. <php><![CDATA[
  46. <strong><?php
  47. require_once(dirname(__FILE__) . '/simpletest/autorun.php');
  48. class TestOfLogging extends UnitTestCase {
  49. function testFirstLogMessagesCreatesFileIfNonexistent() {
  50. }
  51. }
  52. ?></strong>
  53. ]]></php>
  54. Vediamo, passo passo, cosa significa.
  55. </p>
  56. <p>
  57. Il costrutto <code>dirname(__FILE__)</code> si limita a garantire
  58. che il path venga interpretato relativamente alla posizione del file corrente.
  59. </p>
  60. <p>
  61. Cos'è il file <em>autorun.php</em>?
  62. Questo file si occupa di rendere disponibile la definizione di
  63. <code>UnitTestCase</code>.
  64. Recupera tutte le classi dei test definite nel file corrente
  65. e le esegue automagicamente.
  66. Per ottenere questo risultato, imposta un handler di uscita dallo
  67. script PHP.
  68. Approfondiremo il discorso più tardi quando si affronterà
  69. l'argomento della modifica dell'output.
  70. </p>
  71. <p>
  72. I test stessi sono raccolti in classi di test case.
  73. La classe dell'esempio, <code>TestOfLogging</code>, estende, come nel caso
  74. più tipico, <code>UnitTestCase</code>.
  75. Quando il test case viene invocato, l'autorunner si metterà alla ricerca
  76. dei metodi della classe il cui nome inizi per &quot;test&quot;.
  77. Ciascuno di questi metodi verrà eseguito nell'ordine in cui è
  78. definito nella classe.
  79. </p>
  80. <p>
  81. L'unico metodo attualmente presente nella classe è
  82. <code>testFirstLogMessagesCreatesFileIfNonexistent()</code>.
  83. Non contiene ancora niente.
  84. </p>
  85. <p>
  86. Adesso, la definizione vuota del metodo non può eseguire alcunché.
  87. Abbiamo bisogno di riempirla con del codice.
  88. La classe <code>UnitTestCase</code>, normalmente, è
  89. pensata per generare durante il suo funzionamento degli eventi che
  90. vengono inviati ad un reporter in attesa, attraverso i metodi ereditati da
  91. <code>UnitTestCase</code>.
  92. </p>
  93. <p>
  94. Adesso, aggiungiamo il codice di test:
  95. <php><![CDATA[
  96. <?php
  97. require_once(dirname(__FILE__) . '/simpletest/autorun.php');
  98. require_once('../classes/log.php');</strong>
  99. class TestOfLogging extends UnitTestCase {
  100. function testFirstLogMessagesCreatesFileIfNonexistent() {<strong>
  101. @unlink(dirname(__FILE__) . '/../temp/test.log');
  102. $log = new Log(dirname(__FILE__) . '/../temp/test.log');
  103. $log->message('Should write this to a file');
  104. $this->assertTrue(file_exists(dirname(__FILE__) . '/../temp/test.log'));</strong>
  105. }
  106. }
  107. ?>
  108. ]]></php>
  109. </p>
  110. <p>
  111. Starai probabilmente pensando che si tratti di troppo codice
  112. per un singolo collaudo e su questo concordo.
  113. Non temere. Questo è un costo una tantum a da questo momento in poi
  114. i test saranno uno scherzo da aggiungere.
  115. E saranno ancora più semplici con i meccanismi che vedremo
  116. più avanti.
  117. </p>
  118. <p>
  119. Può anche darsi che tu abbia pensato che
  120. <code>testFirstLogMessagesCreatesFileIfNonexistent</code>
  121. sia un nome orribile e troppo lungo per un metodo.
  122. Generalmente questo sarebbe vero, ma in questo contesto è una
  123. buona scelta.
  124. Non capiterà mai più di dover digitare nuovamente questa stringa ed
  125. il nome lungo permette di risparmiare commenti e specifiche.
  126. </p>
  127. <p>
  128. Adesso è il momento della nostra prima decisione.
  129. Il nostro file si chiama <em>log_test.php</em> (qualsiasi nome sarebbe
  130. andato bene) e si trova in una directory chiamata <em>tests</em> (qualsiasi
  131. posizione sarebbe andata ugualmente bene).
  132. Abbiamo chiamato il file del codice da collaudare <em>log.php</em> e
  133. lo abbiamo posizionato in una directory chiamata <em>classes</em>, il che
  134. significa che ci apprestiamo a scrivere una classe, ok?
  135. </p>
  136. <p>
  137. In questo esempio è quello che faremo, ma lo unit tester non
  138. è limitato al solo collaudo delle classi.
  139. Il fatto è che il codice orientato agli oggetti e semplice
  140. da decomporre e da ridisegnare per permettere i collaudi.
  141. Non è un caso che lo stile di collaudo granulare degli unit test
  142. sia storicamente emerso dalla comunità degli sviluppatori object
  143. oriented.
  144. </p>
  145. <p>
  146. Il test di per se' è minimale.
  147. Per prima cosa elimina qualsiasi file di test dovesse
  148. essere rimasto da precedenti esecuzioni.
  149. Le decisioni di design sopraggiungono rapidamente e tutte insieme.
  150. La nostra classe si chiama <code>Log</code> ed
  151. accetta il path del file nel costruttore.
  152. Noi creiamo un log ed immediatamente gli inviamo
  153. un messaggio con il metodo
  154. <code>message()</code>.
  155. Sfortunatamente, la capacità di scegliere nomi originali
  156. non è una caratteristica molto richiesta agli sviluppatori
  157. software.
  158. </p>
  159. <p>
  160. L'elemento base di uno unit test è l'assert.
  161. Qui si desidera assicurarsi che il log file al quale si è
  162. inviato il messaggio venga veramente creato.
  163. <code>UnitTestCase::assertTrue()</code>
  164. invia un evento di successo se la condizione viene valutata come
  165. vera ed un evento di fallimento in caso contrario.
  166. Disponiamo di una varietà di differenti assert e di un insieme ancor
  167. più grande se estendiamo i nostri test case.
  168. </p>
  169. <p>
  170. Questa è una lista base:
  171. <table><tbody>
  172. <tr><td><code>assertTrue($x)</code></td><td>Fallisce se $x è falso</td></tr>
  173. <tr><td><code>assertFalse($x)</code></td><td>Fallisce se $x è vero</td></tr>
  174. <tr><td><code>assertNull($x)</code></td><td>Fallisce se $x è impostato</td></tr>
  175. <tr><td><code>assertNotNull($x)</code></td><td>Fallisce se $x è non impostato</td></tr>
  176. <tr><td><code>assertIsA($x, $t)</code></td><td>Fallisce se $x non è una classe del tipo $t</td></tr>
  177. <tr><td><code>assertNotA($x, $t)</code></td><td>Fallisce se $x è una classe del tipo $t</td></tr>
  178. <tr><td><code>assertEqual($x, $y)</code></td><td>Fallisce se $x == $y è falso</td></tr>
  179. <tr><td><code>assertNotEqual($x, $y)</code></td><td>Fallisce se $x == $y è vero</td></tr>
  180. <tr><td><code>assertWithinMargin($x, $y, $m)</code></td><td>Fallisce se abs($x - $y) &lt; $m è falso</td></tr>
  181. <tr><td><code>assertOutsideMargin($x, $y, $m)</code></td><td>Fallisce se abs($x - $y) &lt; $m è vero</td></tr>
  182. <tr><td><code>assertIdentical($x, $y)</code></td><td>Fallisce se $x == $y è falso or se i tipi differiscono</td></tr>
  183. <tr><td><code>assertNotIdentical($x, $y)</code></td><td>Fallisce se $x == $y è vero ed i tipi sono uguali</td></tr>
  184. <tr><td><code>assertReference($x, $y)</code></td><td>Fallisce a meno che $x e $y siano la stessa variabile</td></tr>
  185. <tr><td><code>assertCopy($x, $y)</code></td><td>Fallisce a meno che $x e $y siano copie identiche</td></tr>
  186. <tr><td><code>assertSame($x, $y)</code></td><td>Fallisce a meno che $x e $y siano lo stesso oggetto</td></tr>
  187. <tr><td><code>assertClone($x, $y)</code></td><td>Fallisce a meno che $x e $y siano identici ma siano oggetti separati</td></tr>
  188. <tr><td><code>assertPattern($p, $x)</code></td><td>Fallisce a meno che l'espressione regolare $p soddisfi $x</td></tr>
  189. <tr><td><code>assertNoPattern($p, $x)</code></td><td>Fallisce se l'espressione regolare $p nonn soddisfi $x</td></tr>
  190. <tr><td><code>expectError($x)</code></td><td>Fallisce se non viene generato un errore che soddisfi il matching con $x</td></tr>
  191. <tr><td><code>expectException($x)</code></td><td>Fallisce se non viene lanciata un'eccezione che soddisfi il matching con $x</td></tr>
  192. <tr><td><code>ignoreException($x)</code></td><td>Ignora qualsiasi eccezione dovesse essere lanciata</td></tr>
  193. </tbody></table>
  194. </p>
  195. <p>
  196. Adesso siamo pronti ad eseguire lo script di test puntandovi il browser.
  197. Cosa succederà?
  198. Dovrebbe essere visualizzato un crash:
  199. <div class="demo">
  200. <b>Fatal error</b>: Failed opening required '../classes/log.php' (include_path='') in <b>/home/marcus/projects/lastcraft/tutorial_tests/Log/tests/log_test.php</b> on line <b>7</b>
  201. </div>
  202. La ragione è che <em>log.php</em> non è ancora stato creato.
  203. </p>
  204. <p>
  205. Aspetta, ma questo è assurdo!
  206. Non starai mica cercando di costruire un collaudo senza ancora avere una sola riga del
  207. codice che vuoi collaudare, vero?
  208. </p>
  209. </section>
  210. <section name="tdd" title="Test Driven Development">
  211. <p>
  212. Il coinventore di <a href="http://www.extremeprogramming.org/">eXtreme Programming</a>,
  213. Kent Beck, ha pubblicato un nuovo manifesto.
  214. Il libro si intitola
  215. <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0321146530/ref=lib_dp_TFCV/102-2696523-7458519?v=glance&amp;s=books&amp;vi=reader#reader-link">Test driven development</a>
  216. (TDD) e promuove lo unit testing ad una posizione chiave nel design di software.
  217. In breve, l'idea è di scrivere dei piccoli test prima e
  218. solo dopo occuparsi della scrittura del codice.
  219. Per qualsiasi codice.
  220. Fidatevi, funziona.
  221. </p>
  222. <p>
  223. Scrivi un nuovo test e fai in modo che passi con successo.
  224. Quello che otterrai all'inizio è un po' di ridondanza e, generalmente, del codice
  225. di qualità schifosa.
  226. Assicurandoti che i test continuino a passare ti puoi preoccupare, allora, di ri-arrangiare
  227. il codice, cioè di fare il refactoring.
  228. Questo ti garantisce di non compromettere le funzionalità.
  229. Una volta che il codice è pulito quanto possibile sei pronto ad aggiungere
  230. nuove funzionalità, ma non lo farai: aggiungerai, invece, un altro test
  231. per la nuova feature e ricomincerai nuovamente il ciclo.
  232. Le funzionalità verranno generate attraverso i tentativi di far
  233. passare con successo i test che le definiscono.
  234. </p>
  235. <p>
  236. Immagina il test come una specifica eseguibile che sia stata creata per l'occasione.
  237. </p>
  238. <p>
  239. E' un approccio radicale e personalmente mi sembra incompleto ma
  240. è molto efficace per spiegare cosa sia uno unit tester.
  241. Nel nostro caso abbiamo un test che fallisce (per non dire che va in crash)
  242. così avremo bisogno di scrivere del codice in <em>log.php</em>:
  243. <php><![CDATA[
  244. <strong><?php
  245. class Log {
  246. function __construct($path) {
  247. }
  248. function message($message) {
  249. }
  250. }
  251. ?></strong>
  252. ]]></php>
  253. Questo è il minimo da fare per evitare fatal error di PHP.
  254. Adesso recuperiamo il risultato:
  255. <div class="demo">
  256. <h1>TestOfLogging</h1>
  257. <span class="fail">Fail</span>: testFirstLogMessagesCreatesFileIfNonexistent-&gt;True assertion failed.<br />
  258. <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete.
  259. <strong>0</strong> passes, <strong>1</strong> fails and <strong>0</strong> exceptions.</div>
  260. </div>
  261. &quot;TestOfLogging&quot; ha fallito.
  262. SimpleTest, di default, utilizza i nomi delle classi per descrivere
  263. i test ma è sempre possibile sostituire il nome con uno di nostra scelta:
  264. <php><![CDATA[
  265. class TestOfLogging extends UnitTestCase {
  266. <strong>function __construct() {
  267. parent::__construct('Log test');
  268. }</strong>
  269. function testFirstLogMessagesCreatesFileIfNonexistent() {<strong>
  270. @unlink(dirname(__FILE__) . '/../temp/test.log');
  271. $log = new Log(dirname(__FILE__) . '/../temp/test.log');
  272. $log->message('Should write this to a file');
  273. $this->assertTrue(file_exists(dirname(__FILE__) . '/../temp/test.log'));</strong>
  274. }
  275. }
  276. ]]></php>
  277. Il che fornisce:
  278. <div class="demo">
  279. <h1>Log test</h1>
  280. <span class="fail">Fail</span>: testFirstLogMessagesCreatesFileIfNonexistent-&gt;File created.<br />
  281. <div style="padding: 8px; margin-top: 1em; background-color: red; color: white;">1/1 test cases complete.
  282. <strong>0</strong> passes, <strong>1</strong> fails and <strong>0</strong> exceptions.</div>
  283. </div>
  284. Se si ha intenzione di cambiare il nome del test si deve intervenire
  285. modificando l'output del reporter, operazione che affronteremo più tardi.
  286. </p>
  287. <p>
  288. Per far passare il test si potrebbe semplicemente creare il file
  289. nel costruttore di <code>Log</code>.
  290. Questa tecnica d'&quot;inganno&quot; è molto utile per controllare
  291. che i test funzionino, a mali estremi.
  292. Questo è particolarmente vero nel caso si siano registrati fallimenti dei
  293. test e ci si voglia assicurare di non aver dimenticato qualcosa di
  294. stupido.
  295. Non procederemo così lentamente, perciò:
  296. <php><![CDATA[
  297. <?php
  298. class Log {<strong>
  299. var $path;</strong>
  300. function __construct($path) {<strong>
  301. $this->path = $path;</strong>
  302. }
  303. function message($message) {<strong>
  304. $file = fopen($this->path, 'a');
  305. fwrite($file, $message . "\n");
  306. fclose($file);</strong>
  307. }
  308. }
  309. ?>
  310. ]]></php>
  311. Ho dovuto incontrare non meno di quattro failure per raggiungere questo passo:
  312. non avevo creato la directory temporanea, non l'avevo impostata come
  313. pubblicamente accessibile in scrittura, avevo un errore di battitura e mi
  314. ero dimenticato di aggiungere la nuova cartella sul CVS (questo l'ho scoperto più
  315. tardi).
  316. Ognuno di questi problemi avrebbe potuto tenermi occupato per diverse ore
  317. se solo si fosse manifestato in un secondo momento, ma è proprio per
  318. questo che i test servono.
  319. </p>
  320. <p>
  321. Con le correzioni necessarie si è ottenuto:
  322. <div class="demo">
  323. <h1>Log test</h1>
  324. <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
  325. <strong>1</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div>
  326. </div>
  327. Successo!
  328. </p>
  329. <p>
  330. Può darsi che tu non gradisca lo stile di visualizzazione minimale.
  331. Di default, i successi non vengono mostrati perché, generalmente, non c
  332. bisogno di ulteriori informazioni quando si ha la situazione
  333. sotto controllo.
  334. Se non si sa come le cose stanno procedendo, allora è il caso di scrivere
  335. ulteriori test.
  336. </p>
  337. <p>
  338. Ok, qui sono stato fin troppo rigido.
  339. Se desideri vedere anche i casi di successo allora ti basta
  340. <a local="display_subclass_tutorial">estendere la classe
  341. <code>HtmlReporter</code></a>
  342. ed aggiungerla ai test al posto del reporter standard.
  343. Anche io gradisco il comfort, ogni tanto.
  344. </p>
  345. </section>
  346. <section name="doc" title="I test come documentazione">
  347. <p>
  348. Adesso una sottigliezza.
  349. Non vogliamo che il file creato esista fino a che non
  350. inviamo il messaggio.
  351. Piuttosto che pensarci troppo sopra, aggiungeremo
  352. intanto l'assert:
  353. <php><![CDATA[
  354. class TestOfLogging extends UnitTestCase {
  355. function testFirstLogMessagesCreatesFileIfNonexistent() {
  356. @unlink(dirname(__FILE__) . '/../temp/test.log');
  357. $log = new Log(dirname(__FILE__) . '/../temp/test.log');<strong>
  358. $this->assertFalse(file_exists(dirname(__FILE__) . '/../temp/test.log'));</strong>
  359. $log->message('Should write this to a file');
  360. $this->assertTrue(file_exists(dirname(__FILE__) . '/../temp/test.log'));
  361. }
  362. }
  363. ]]></php>
  364. per scoprire che già funzionava così:
  365. <div class="demo">
  366. <h1>TestOfLogging</h1>
  367. <div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete.
  368. <strong>2</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div>
  369. </div>
  370. Ora, sapevo che avrebbe funzionato.
  371. Ho aggiunto il test che lo conferma in parte per mettere in pace l'animo e in parte
  372. per documentare questo comportamento.
  373. Questa unica linea extra dice molto di più, in questo contesto, di dozzine
  374. di righe di use case o di un intero diagramma di attività UML.
  375. Che la test suite sia una sorgente di documentazione è un piacevole
  376. effetto collaterale di tutti i test.
  377. </p>
  378. <p>
  379. Dovremmo pulire il file temporaneo alla fine del test?
  380. Di solito faccio questa operazione quando ho terminato
  381. il metodo di test e vedo che funziona.
  382. Non ho voglia di inserire in CVS del codice che lasci in giro
  383. residui di file di test dopo i collaudi.
  384. Non voglio farlo mentre scrivo i test, comunque.
  385. Probabilmente dovrei, ma a volte ho bisogno di vedere come
  386. le cose procedono e, poi, è una questione di comodità.
  387. </p>
  388. <p>
  389. Nella progetti reali si produce più di un test case e, pertanto,
  390. il prossimo argomento che dobbiamo trattare è
  391. <a local="group_test_tutorial">il raggruppamento dei test in test suite.</a>.
  392. </p>
  393. </section>
  394. </content>
  395. <internal>
  396. <link>Creating a <a href="#new">new test case</a>.</link>
  397. <link><a href="#tdd">Test driven development</a> in PHP.</link>
  398. <link><a href="#doc">Tests as documentation</a> is one of many side effects.</link>
  399. </internal>
  400. <external>
  401. <link>
  402. The <a href="http://junit.sourceforge.net/doc/faq/faq.htm">JUnit FAQ</a>
  403. has plenty of useful testing advice.
  404. </link>
  405. <link>
  406. <a href="group_test_tutorial.php">Next</a> is grouping test
  407. cases together.
  408. </link>
  409. <link>
  410. You will need the <a href="simple_test.php">SimpleTest testing framework</a>
  411. for these examples.
  412. </link>
  413. </external>
  414. <meta>
  415. <keywords>
  416. software development,
  417. php programming,
  418. programming php,
  419. software development tools,
  420. php tutorial,
  421. free php scripts,
  422. architecture,
  423. php resources,
  424. mock objects,
  425. junit,
  426. php testing,
  427. unit test,
  428. automated php testing,
  429. test cases tutorial,
  430. explain unit test case,
  431. unit test example,
  432. unit test
  433. </keywords>
  434. </meta>
  435. </page>