Ormai è un dato di fatto che ognuno preferisca adottare le proprie tecniche quando si siede davanti al proprio computer per scrivere del codice, sia questo per lavoro o per puro divertimento. Purtroppo è anche un dato di fatto che spesso le metodologie adottate, seppur piacciano tanto alla persona che le sta mettendo in pratica, non sono da considerarsi ottimali e spesso, all'insaputa del programmatore, contribuiscono ai tipici ritardi ed alle solite vagonate di bug che accompagnano la prima release di molto software.
Purtroppo nessuno ha ancora definito quale sia la pratica corretta da seguire quando si affronta un progetto che comprende lo sviluppo di un software, e probabilmente nessuno troverà mai una strada univoca dato che le situazioni e le condizioni di lavoro spesso influiscono pesantemente sulle metodologie da adottare. Ma rimane un dato ormai assodato (o comunque ritenuto importante da molto sviluppatori) il fatto che il testing semi-automatico del proprio codice sorgente aiuti di gran lunga tutti i programmatori a rilasciare codice stabile, capace di adattarsi alle patch successive con la certezza che il resto del programma continui a comportarsi bene.
Lo Unit Testing è una pratica che ormai è utilizzata da gran parte dei programmatori, soprattutto da quelli che seguono l'eXtreme Programming e tutte le discipline derivate. Non poteva mancare anche in PHP un framework per automatizzare il testing dei propri sorgenti. Questo framework si chiama PHPUnit ed è un insieme di tool e librerie molto potenti che coprono la maggior parte delle operazioni necessarie se si desidera seguire queste metodologie.
Cos'è lo Unit Testing
Lo Unit Testing è un procedimento utilizzato dai programmatori per assicurarsi che tutte le unità che compongono il codice sorgente di un'applicazione o libreria siano esenti da errori, si comportino in modo uniforme sulle diverse architetture e continuino a funzionare correttamente anche dopo azioni di refactoring o modifiche successive. Come accennato precedentemente effettuare Unit Testing è una pratica molto comune per chi applica le metodologie agili ad i propri progetti e porta sempre ad ottimi risultati.
Normalmente il software viene sviluppato pensando più all'implementazione di tutte le richieste piuttosto che focalizzarsi sulla stabilità dell'intero sistema, che viene testata alla fine del processo di sviluppo. Questo approccio purtroppo porta a seguire un processo di sviluppo che non facilita per nulla il lavoro del programmatore, portandolo a dover spendere molto più tempo nel bugfixing che nello sviluppo. Applicando lo Unit Testing ad i propri sorgenti ogni qual volta un'unità di codice viene aggiunta al repository, ci si assicura che ogni modifica apportata non abbia intaccato le altre e che il codice aggiunto si comporti proprio come richiesto.
Effettuare Unit Testing è un'operazione semplice, che con poca fatica migliora la qualità del lavoro: seguendo determinate regole si scrivono piccoli pezzi di codice atti a testare che il comportamento di una determinata funzionalità sia proprio come ce lo aspettiamo; poi si aggiungono i nuovi test a quelli precedenti e si eseguono tutti insieme attraverso appositi tool che restituiscono i test falliti e quelli riusciti. Alcuni tool informano anche sulle porzioni di codice non testate in modo che il programmatore possa scrivere nuovi test ed assicurarsi che tutto il suo codice si comporti correttamente. Isolando l funzionalità si scrivono dei test che assicurano queste funzionino correttamente e quindi il software risultante spesso sarà già stabile ed oltretutto ad ogni successiva modifica potremo eseguire nuovamente tutti i test in modo da assicurarci che il comportamento dell'intero sistema sia rimasto corretto.
PHPUnit all'opera
PHPUnit è un framework scritto da Sebastian Bergmann per lo Unit testing in PHP. È distribuito attraverso PEAR e si compone di uno script in linea di comando per eseguire i test ed una libreria per guidare nella scrittura di questi ultimi. Oltretutto la documentazione della libreria è ben fatta e sempre aggiornata, e copre tutte le funzionalità fornite da PHPUnit con un buon livello di dettaglio.
L'obiettivo principale è facilitare la scrittura di test ed il loro riutilizzo, in modo che il programmatore non debba perdere troppo tempo in tutte quelle operazioni di contorno che potrebbero rendere tedioso il lavoro ed allontanare alcune persone da questa pratica. Quando si decide di fare Unit Testing su del codice sorgente solitamente si procede in questo modo:
- si isola ogni singola funzionalità implementata;
- si scrivono dei brevi ma completi snippet di codice che testino ogni funzionalità isolata, sia per quanto riguarda i casi di funzionamento corretto che quelli di funzionamento errato;
- si raggruppano gli snippet (che saranno scritti seguendo le regole imposte dalla libreria che si sta utilizzando) in TestCase;
- si raggruppano i TestCase in TestSuite, che rappresentano una macro funzionalità rappresentata dal nostro codice;
- eventualmente si raggruppano le TestSuite tra di loro;
- si esegue il codice di testing correggendo gli errori riscontrati ed assicurandosi che tutto il codice sia correttamente testato;
Ogni singolo step (a parte forse l'isolamento delle funzionalità) è influenzato dalla libreria che si decide di utilizzare.
Per prima cosa è necessario procedere con l'installazione di PHPUnit utilizzando PEAR, ricordandosi di aggiungere come sorgente in cui ricercare i repository il sito ufficiale di PHPUnit:
pear channel-discover pear.phpunit.de pear install phpunit/PHPUnit
In caso di problemi rimando comunque alla documentazione ufficiale segnalata precedentemente.
Una volta scaricata la libreria, questa sarà installata all'interno della directory specificata in fase di configurazione di PEAR; basterà aggiungere questa directory al proprio include_path
e si potranno subito usare le librerie per scrivere i propri test.
Cominciamo con un piccolo esempio in cui definiamo una semplice classe ed effettuiamo i test che controllano il corretto funzionamento dei due metodi definiti. Lo vediamo nella pagina successiva.
<?php // Supponiamo che PHPUnit sia stato installato correttamente // e che il path in cui è installato PEAR sia incluso // nella direttiva include_path require_once 'PHPUnit/Framework.php'; // MyCustomObject dovrebbe essere definito in un altro file ed // incluso per il testing class MyCustomObject { private $value; public function __construct( $value ) { $this->value = $value; } public function gt( $other ) { return ( $this->value > $other ); } public function getValue() { return $this->value; } } class MyCustomObjectTest extends PHPUnit_Framework_TestCase { public function testNewCustomObjectGetValue() { $fixture = new MyCustomObject( 20 ); $this->assertEquals( 20, $fixture->getValue() ); } public function testNewCustomObjectIsGreaterThen() { $fixture = new MyCustomObject( 10 ); $this->assertTrue( $fixture->gt( 5 ) ); } public function testNewCustomObjectIsNotGreaterThen() { $fixture = new MyCustomObject( 10 ); $this->assertFalse( $fixture->gt( 15 ) ); } } ?>
Il test definito implementa tre test che controllano il corretto funzionamento delle operazioni definite nella classe MyCustomObject
. Da questo semplice esempio possiamo trarre alcune informazioni utili per la definizione dei test:
- La classe che rappresenta il TestCase ha il nome della classe da testare seguito da Test; è una convenzione utile per identificare l'unità sulla quale si sta lavorando;
- La classe di test estende
PHPUnit_Framework_TestCase
(o una sua sottoclasse in caso abbiate implementato funzionalità aggiuntive per arricchire i vostri test); - I singoli snippet di codice che rappresentano un test sono dei metodi pubblici che non accettano parametri ed iniziano con la parola
test*
; - All'interno del codice, per controllare che le operazioni eseguite abbiano avuto il risultato corretto, vengono utilizzati i metodi
assert*
. Questi metodi controllano che un'operazione abbia fornito un risultato adeguato quando eseguita;
Definito il test possiamo procedere con l'esecuzione utilizzando il tool a linea di comando fornito con PHPUnit. Questo tool accetta numerose opzioni che permettono di controllare il comportamento dello Unit test che vedremo in seguito, e viene installato quando si effettua l'installazione del package attraverso PEAR.
Per effettuare uno Unit test è necessario eseguire questo comando:
phpunit MyCustomObjectTest
che con tutta probabilità stamperà questo valore:
PHPUnit 3.1.4 by Sebastian Bergmann. ... Time: 0 seconds OK (3 tests)
Specificando il comando PHPUnit con un solo parametro dobbiamo utilizzare come parametro il nome del file che contiene il TestCase, che sarà anche il nome della classe che implementa i test. I test verranno eseguiti uno alla volta ed in output otterremo il risultato. PHPUnit restituisce un punto nel caso il test sia stato eseguito correttamente, la lettere F in caso di fallimento del test, la lettera S in caso il test venga saltato e la lettera E nel caso in cui il test produca degli errori PHP non gestiti.
Abbiamo terminato l'introduzione al framework PHPUnit; nei prossimi articoli ci addentreremo in dettaglio nelle funzionalità aggiuntive fornite dal framework.