Lo Unit Test

Documentazione sui test di regressione con SimpleTest per PHP

# unit Gli Unit Test Case

Il cuore di SimpleTest è un framework di test di regressione costruito intorno a classi di test case. Un tipico test case ha questo aspetto:

<strong>class FileTestCase extends UnitTestCase { }</strong> I test vengono aggiunti nel test case come metodi i cui nomi inizino per default con la stringa "test" in modo che quando il test case viene invocato essi vengano lanciato nello stesso ordine in cui l'introspezione di PHP li trova. Possono essere aggiunti quanti metodi di test si vuole.

Per esempio: require_once('simpletest/autorun.php'); require_once('../classes/writer.php'); class FileTestCase extends UnitTestCase { function FileTestCase() { $this->UnitTestCase('File test'); }<strong> function setUp() { @unlink('../temp/test.txt'); } function tearDown() { @unlink('../temp/test.txt'); } function testCreation() { $writer = &new FileWriter('../temp/test.txt'); $writer->write('Hello'); $this->assertTrue(file_exists('../temp/test.txt'), 'File created'); }</strong> } Il costruttore è opzionale e normalmente viene omesso. In assenza di un nome, il test case prende il nome della classe.

L'unico metodo di test di cui disponiamo al momento è `testCreation()` con il quale verificiamo che un file sia stato creato dall'oggetto `Writer`. Avremmo anche potuto mettere l'istruzione `unlink()` nall'interno del metodo, ma l'utilizzo di `setUp()` e `tearDown()` ci permette di condividerlo con gli altri metodi di test che aggiungeremo nel futuro.

Il metodo `setUp()` viene sistematicamente eseguito prima dell'esecuzione di ogni metodo di test mentre `tearDown()` viene eseguito immediatamente dopo il termine della loro esecuzione.

E' possibile utilizzare il costruttore per ospitare del codice di set up in modo che venga eseguito una sola volta per tutti i metodi del test case ma in questo modo di rischia di provocare delle interferenze tra i test. La modalità utilizzata è leggermente più lenta ma è più sicura. Gli utenti che utilzzano JUnit facciano attenzione che questo comportamento è diverso da quanto potrebbero attendersi. Sorprendentemente, infatti, JUnit instanzia nuovamente l'intero test case in corrispondenza dell'esecuzione di ogni test per evitare interferenze. SimpleTest richiede che l'utente finale utilizzi `setUp()` anche se alcuni hook addizionali vengono forniti per gli sviluppatori di librerie.

Per visualizzare il risultato di un test viene utilizzata una classe di reportistica che viene avvisata dei risultati per mezzo dei metodi del tipo `assert...()`. Di seguito è riportato l'elenco completo dei metodi assert disponibili nella classe `UnitTestCase`, la classe standard di SimpleTest:

`assertTrue($x)` | Fallisce se $x è falso
`assertFalse($x)` | Fallisce se $x è vero
`assertNull($x)` | Fallisce se $x è impostato
`assertNotNull($x)` | Fallisce se $x è non impostato
`assertIsA($x, $t)` | Fallisce se $x non è una classe del tipo $t
`assertNotA($x, $t)` | Fallisce se $x è una classe del tipo $t
`assertEqual($x, $y)` | Fallisce se $x == $y è falso
`assertNotEqual($x, $y)` | Fallisce se $x == $y è vero
`assertWithinMargin($x, $y, $m)` | Fallisce se abs($x - $y) < $m è falso
`assertOutsideMargin($x, $y, $m)` | Fallisce se abs($x - $y) < $m è vero
`assertIdentical($x, $y)` | Fallisce se $x == $y è falso or se i tipi differiscono
`assertNotIdentical($x, $y)` | Fallisce se $x == $y è vero ed i tipi sono uguali
`assertReference($x, $y)` | Fallisce a meno che $x e $y siano la stessa variabile
`assertClone($x, $y)` | Fallisce a meno che $x e $y siano copie identiche
`assertPattern($p, $x)` | Fallisce a meno che l'espressione regolare $p soddisfi $x
`assertNoPattern($p, $x)` | Fallisce se l'espressione regolare $p nonn soddisfi $x
`expectError($x)` | Fallisce se non viene generato un errore che soddisfi il matching con $x
`expectException($x)` | Fallisce se non viene lanciata un'eccezione che soddisfi il matching con $x
`ignoreException($x)` | Ignora qualsiasi eccezione dovesse essere lanciata
`assert($e)` | Fallisce se l'oggetto expectation $e fallisce

Tutti i metodi assert accettano come ultimo parametro opzionale una descrizione che verrà utilizzata come label del risultato visualizzato nel report. Nel caso sia omesso, il messaggio di default viene inviato e normalmente questo è sufficientemente esplicativo. Il messaggio di default può essere incluso nel proprio messaggio usando "%s" all'interno della stringa. Tutti gli assert restituiscono true in caso di test passato con successo e false in caso di fallimento.

Alcuni esempi: $variable = null; <strong>$this->assertNull($variable, 'Should be cleared');</strong> passa sempre con successo e normalmente non visualizza alcun messaggio Nel caso si sia impostato il tester per visualizzare un rapporto sui test passati con successo anche il messaggio di questo test verrebbe visualizzato. <strong>$this->assertIdentical(0, false, 'Zero is not false [%s]');</strong> Questo test fallisce dal momento che esegue, oltre alla comparazione dei due valori, anche un controllo sul tipo.

La parte "%s" viene rimpiazzata con il messaggio di default, lo stesso che verrebbe mosrato se non se fosse specificato uno esplicitamente. $a = 1; $b = $a; <strong>$this->assertReference($a, $b);</strong> Fallisce dal momento che la variabile `$a` è uan copia di `$b`. <strong>$this->assertPattern('/hello/i', 'Hello world');</strong> Questo test passa perché si sta verificando, con un matching case insensitive, se la stringa `hello` è contenuta in `Hello world`. <strong>$this->expectError();</strong> trigger_error('Catastrophe'); Qui il controllo intercetta il messaggio "Catastrophe" senza fare alcun controllo sul testo. Questa operazione rimuove l'errore dalla coda. <strong>$this->expectError('Catastrophe');</strong> trigger_error('Catastrophe'); Questo test non si limita ad intercettare l'esistenza dell'errore ma ne verifica anche il testo contenuto: in questo questo caso il testo coincide e quindi il test passa. Se al termine del metodo di test dovessero restare errori non intercettati verrebbe riportata un'eccezione nel test.

Si noti che SimpleTest non è in grado di intercettare gli errori a compile time del PHP.

I test case possiedono anche alcuni metodi comodi per eseguire il debugging del codice e per estendere la suite:

`setUp()` | Viene eseguita prima di ogni metodo di test
`tearDown()` | Viene eseguita dopo ogni metodo di test
`pass()` | Fa passare un test
`fail()` | Invia un messaggio di test fallito
`error()` | Genera un evento di errore
`signal($type, $payload)` | Invia un messaggio definito dell'utente al test reporter
`dump($var)` | Esegue un `print_r()` formattato nel caso si debbano fare rapidamente e grossolanamente un debug

# extending_unit Estendere i Test Case

Naturalmente è possibile aggiungere metodi addizionali in modo da creare dei tipi di test specifici ed estendere il framework: require_once('simpletest/autorun.php'); <strong> class FileTester extends UnitTestCase { function FileTester($name = false) { $this->UnitTestCase($name); } function assertFileExists($filename, $message = '%s') { $this->assertTrue( file_exists($filename), sprintf($message, 'File [$filename] existence check')); }</strong> } In questo esempio si presuppone che la libreria SimpleTest sia conservata nella directory locale simpletest. Per far funzionare l'esempio, modificare con il path più adeguato.

Per evitare che questo test case venga accidentamente eseguito, è possibile marcare la classe come `abstract`.

In alternativa è possibile usare la direttiva `SimpleTestOptions::ignore('FileTester');` in qualche punto del codice.

Adesso è possibile ereditare da questo nuovo case esattamente come da ogni altro case test. class FileTestCase extends <strong>FileTester</strong> { function setUp() { @unlink('../temp/test.txt'); } function tearDown() { @unlink('../temp/test.txt'); } function testCreation() { $writer = &new FileWriter('../temp/test.txt'); $writer->write('Hello');<strong> $this->assertFileExists('../temp/test.txt');</strong> } }

Se si desiderasse un test case che non disponesse di una sola parte degli assert di `UnitTestCase` e di altri assert custom, si può estendere `SimpleTestCase`. Si dovrebbe quindi includere il file simple_test.php invece del file unit_tester.php.

Si veda oltre nel caso si desideri incorporare test case di altri unit tester nelle proprie test suite.

# running_unit Eseguire un semplice Test Case

Se si esclude il caso in cui stia impazzendo con un modulo particolarmente ostico e non si voglia disturbare l'intera test suite, capiterà raramente di lanciare un solo test case. Con autorun non c'è bisogno di una preparazione particolare: è sufficiente lanciare uno specifico file di test ed il gioco è fatto.

Quando si lancia singolarmente uno specifico file è perfino possibile decidere quale report usare (per esempio, scegliendo tra `TextReporter` o `HtmlReporter`)

<?php require_once('simpletest/autorun.php');<strong> SimpleTest :: prefer(new TextReporter());</strong> require_once('../classes/writer.php'); class FileTestCase extends UnitTestCase { ... } ?> Questo script verrà eseguito così com'è, ma naturalmente non visualizzerà alcun messagio di successo o fallimento finché non vi vengono aggiunti metodi di test.

Unit test cases and basic assertions. Extending test cases to customise them for your own project. Running a single case as a single script.
SimpleTest project page on SourceForge. SimpleTest download page on LastCraft. Full API for SimpleTest from the PHPDoc.
php unit testing, test integration, documentation, marcus baker, simpletest, simpletest documentation, phpunit, junit, xunit, agile web development, eXtreme Programming, Test Driven, TDD