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

CakePHP-Unit-Testing-TDD-Applicazione-di-Esempio-Rubrica-Schema

 

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.

CakePHP-Unit-Testing-TDD-Applicazione-di-Esempio-Rubrica-Testcase-Contact

Il test naturalmente fallirà perché il metodo getAllContacts() non ritorna i dati nel formato da noi richiesto.

CakePHP-Unit-Testing-TDD-Applicazione-di-Esempio-Rubrica-Testcase-Contact-getAllContacts-Fail001

Modifichiamo il metodo getAllContacts()

Lanciamo di nuovo il test.

CakePHP-Unit-Testing-TDD-Applicazione-di-Esempio-Rubrica-Testcase-Contact-getAllContacts-Success001

Ecco il nostro primo test passato con successo! Nel prossimo articolo scriveremo i test per il dettaglio del contatto, l’aggiunta e la modifica.

 

Seguimi

Walter Raponi

Appassionato di torte!

Ingredienti base: Model, View, Controller! Un grande framework come CakePHP e la nostra torta è pronta!
Seguimi

Latest posts by Walter Raponi (see all)