In Questa Pagina
Dopo una lunga assenza ci sentiamo in dovere di proporre un argomento importante. Questo è il primo di una serie di articoli dedicato allo Unit Testing. Svilupperemo un’applicazione rubrica di esempio utilizzando il Test Driven Development (Sviluppo Guidato dai Test). Il Test Driven Development (da ora in poi TDD) non è l’unica maniera di eseguire lo Unit Testing sul nostro codice, ma entreremo nel dettaglio tra qualche paragrafo. Il progetto è basato sulla release 2.7.1 di CakePHP.
Cos’è lo Unit testing?
Per unit testing si intende l’attività di testing di singole unità software. Per unità si intende normalmente il minimo componente di un programma dotato di funzionamento autonomo; a seconda del paradigma di programmazione o linguaggio di programmazione, questo può corrispondere per esempio a una singola funzione nella programmazione procedurale, o una singola classe o un singolo metodo nella programmazione a oggetti. (fonte Wikipedia)
Dividere il nostro codice, indifferentemente dal paradigma utilizzato, in piccole unità da testare. I vari test vengono chiamati Test Case. Lo unit testing in CakePHP si appoggia alla libreria più diffusa nel mondo PHP: PHPUnit.
Non tratterò l’installazione in questa serie perché non c’è molto da dire. Qui la pagina del Cookbook sull’argomento e sull’installazione attraverso composer o phar.
Applicazione di Esempio: Rubrica
La nostra rubrica sarà composta da 5 tabelle ed utilizzeremo la tecnica del TDD per lo sviluppo.
- Ogni contatto avrà un nome ed un cognome.
- Ogni contatto può avere zero o più numeri telefonici.
- Ogni numero telefonico appartiene ad un contatto e può essere associato ad una categoria telefonica (casa, ufficio, fax etc…).
- Ogni contatto può avere zero o più indirizzi email associati.
- Ogni indirizzo email appartiene ad un contatto e può appartenere ad una categoria email.
- Se viene cancellato un contatto verranno cancellati automaticamente i numeri telefonici ed indirizzi email associati
- Se viene cancellata una categoria telefonica il campo phonetag_id nella tabella phones verrà impostato a null
- Se viene cancellata una categoria email il campo mailtag_id nella tabella mails verrà impostato a null
- Anche se gli indirizzi email ed i numeri telefonici appartengono ad un singolo contatto non c’è controllo di unicità sul record. Può infatti capitare che un numero telefonico appartenga a più persone, come un numero di un centralino. Lo stesso discorso è valido per gli indirizzi email, pensiamo alle varie caselle info@dominio o amministrazione@dominio.
Di seguito la struttura delle tabelle
SQL delle Tabelle
Possiamo ora eseguire il bake per creare tutti i dati o in alternativa creare i modelli ed i controller dentro /app.
Prima di procedere con il prossimo passo c’è la necessità di configurare il database di test nel file /app/Config/database.php e modificare la proprietà $test. Il livello di debug globale deve essere almeno 1 ed impostato nel file /app/Config/core.php.
Attenzione, non utilizzare il database utilizzato per sviluppare o attualmente in produzione. Nei test case seguenti infatti le tabelle verranno create dai test, riempite con dei dati da noi definiti e poi gettate. Se la tabella è preesistente verrà cancellata, creata e riempita con i dati.
Fixtures
Le fixtures ci consentono di proteggere ed isolare i nostri test attraverso la creazione di dati predefiniti. Utilizzando dei dati predefiniti sarà semplice testare il comportamento della nostra applicazione sui dati. Le fixtures sono create in automatico con il bake dei modelli (se utilizzato). Creiamo ora il primo fixture per il nostro modello Contact.
Le fixture vanno inserite nella cartella /app/Test/Fixture. Anche qui CakePHP segue il motto “convention over configuration”. I file e le classi delle fixtures devono seguire la nomenclatura NomeclasseFixture, quindi nel nostro caso ContactFixture.
Ecco il contenuto del file
Tutte le fixtures estendono la classe CakeTestFixture. La proprietà pubblica $fields contiene la definizione dei nostri campi e rispecchia quindi la struttura della tabella ed ha lo stesso formato utilizzato per definire CakeSchema. Le chiavi a disposizione per la definizione delle tabelle sono:
type
- string (varchar)
- text
- integer (int)
- float
- datetime
- timestamp
- time
- date
- binary (blob)
key
- Impostare a primary per rendere il campo PRIMARY KEY e AUTO_INCREMENT
length
- definisce la lunghezza del campo
null
- impostare a true per permettere valori null
default
- valore di default del campo
Nella nostra fixture abbiamo definito tre campi: id come auto increment e chiave primary, name come stringa not null e surname come stringa not null.
Troviamo poi la proprietà $records che conterrà i nostri valori preimpostati, il codice si commenta da solo.
Testare i Modelli
Ecco il nostro primo test case utilizzando la tecnica del TDD, cioè creando prima il test. Questo test fallirà alla prima esecuzione perché il codice per farlo funzionare non è ancora stato scritto. Questo modo di pensare ci porterà via leggermente più tempo in fase di analisi ma darà i suo frutti nel medio/lungo periodo. Creiamo il test, scriviamo il codice per farlo passare, eseguiamo del refactoring sul codice e ci assicuriamo che il test passi ancora. E’ naturalmente necessario scrivere dei buoni test il più vicino possibile alla situazione di utilizzo normale della app.
Utilizzando la tecnica del TDD siamo portati ad inserire più metodi possibile nei modelli così da seguire anche la buona norma “Fat models, skin controllers” consigliata da CakePHP.
Creiamo il file /app/Test/Case/Model/ContactTest.php
Come per le Fixture la nomenclatura è la solita e si rispecchia anche nella classe qui sotto.
La prima cosa che facciamo è importare il modello Contact. Impostiamo poi la proprietà $fixture per caricare la fixture creata in precedenza. Tutte le fixture create da noi sono figlie della root “app”. Carichiamo poi un nuovo oggetto del modello utilizzando ClassRegistry::init(‘Contact’);
Ricordiamoci come prima cosa di richiamare il parent::setup() necessario ad eseguire tutte le operazioni di avvio del test case.
Generiamo ora il nostro primo test. I test sono dei metodi che utilizzano la nomenclatura testNometest. I nostri test seguiranno il nome dei metodi del modello per rendere semplice il riconoscimento del test riuscito o fallito.
Il nostro primo test è concentrato sulla lista dei contatti ed il relativo metodo nel modello
Grazie alla fixture creata in precedenza abbiamo dei dati conosciuti da verificare per assicurarci che il metodo getAllContacts() del modello Contact generi un array corretto per la nostra applicazione. Apriamo il modello Contact e generiamo il metodo getAllContacts() lasciandolo vuoto. Possiamo ora eseguire il nostro test che sarà destinato a fallire. Nella barra degli indirizzi richiamate il file /test.php. Sulla sinistra troviamo il menù con tutti i test disponibili nei Plugins e nel Core. Quello che ci interessa è contenuto nella categoria App/Tests.
Questa categoria conterrà una sola voce, il test case appena creato. Cliccando sul link Model / Contact verrà eseguito il test.
Il test naturalmente fallirà perché il metodo getAllContacts() non ritorna i dati nel formato da noi richiesto.
Modifichiamo il metodo getAllContacts()
Lanciamo di nuovo il test.
Ecco il nostro primo test passato con successo! Nel prossimo articolo scriveremo i test per il dettaglio del contatto, l’aggiunta e la modifica.
- CakePHP 2.8.2 - 03/17/2016
- Reverse Routing - 03/17/2016
- CakePHP 12 Errori da Principiante - 02/24/2016
Per testare le Action dei controller puoi seguire la stessa procedura dei model ma estendendo la classe ControllerTestCase. A breve affronteremo l’argomento. Qui trovi la documentazione: http://book.cakephp.org/2.0/en/development/testing.html#testing-controllers
Per testare un’intera applicazione lo Unit Testing non è l’ideale. Come hai fatto notare qui si testa la parte più piccola disponibile. Potresti provare prodotti del tipo “Selenium” o ci sono altri servizi del genere.
Rimango dell’idea che questi servizi tornano utili solamente per assicurarci che le nostre funzionalità di base non cambino in caso di modifica. Il test effettuato da una persona fisica è sicuramente più affidabile nella ricerca di bugs, non utilizzano mai il software per come l’hai pensato…