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