La guida del principiante allo sviluppo basato sui test

Testare il tuo codice è fastidioso, ma l'impatto di non farlo può essere di ordine di grandezza più fastidioso! In questo articolo, utilizzeremo lo sviluppo basato su test per scrivere e testare il nostro codice in modo più efficace.


Che cos'è lo sviluppo guidato dai test?

Sin dagli albori dell'era dei computer, i programmatori e gli insetti hanno combattuto per la supremazia. È un evento inevitabile. Persino i più grandi programmatori sono preda di queste anomalie. Nessun codice è sicuro. Ecco perché facciamo test. I programmatori, almeno quelli sane, testano il loro codice eseguendolo su macchine di sviluppo per assicurarsi che faccia quello che dovrebbe.


Programmatore sano che mette alla prova i suoi programmi.
Immagine per gentile concessione di http://www.youthedesigner.com
Programmatore folle che non testa i suoi programmi.
Immagine per gentile concessione di http://www.internetannoyanceday.com

Sviluppo guidato dai test è una tecnica di programmazione che richiede di scrivere contemporaneamente codice reale e codice di test automatizzato. Ciò garantisce di testare il codice e consente di ripetere il test del codice in modo rapido e semplice, poiché è automatizzato.

Come funziona?

Lo sviluppo basato sui test, o TDD come lo chiameremo da ora in poi, ruota attorno a un breve ciclo di sviluppo iterativo che va in questo modo:

  1. Prima di scrivere qualsiasi codice, devi prima scrivere un test automatico per il tuo codice. Durante la scrittura dei test automatici, è necessario tenere conto di tutti i possibili input, errori e output. In questo modo, la tua mente non è offuscata da alcun codice che sia già stato scritto.
  2. La prima volta che si esegue il test automatizzato, il test dovrebbe fallire, a indicare che il codice non è ancora pronto.
  3. In seguito, è possibile iniziare la programmazione. Poiché esiste già un test automatico, a condizione che il codice non funzioni, significa che non è ancora pronto. Il codice può essere risolto finché non passa tutte le asserzioni.
  4. Una volta che il codice ha superato il test, è possibile iniziare a ripulirlo, tramite il refactoring. Finché il codice supera ancora il test, significa che funziona ancora. Non devi più preoccuparti delle modifiche che introducono nuovi bug.
  5. Ricominciare da capo con un altro metodo o programma.

Il ciclo di sviluppo basato su test
Immagine per gentile concessione di http://en.wikipedia.org/wiki/Test-driven_development

Ottimo, ma come va meglio dei normali test?

Hai mai intenzionalmente saltato il test di un programma perché:

  • Hai ritenuto che fosse una perdita di tempo da testare, dal momento che era solo un leggero cambiamento di codice?
  • Ti sei sentito pigro a provare di nuovo tutto?
  • Non hai avuto abbastanza tempo per testare perché il project manager voleva che passasse alla produzione al più presto?
  • Ti sei detto che lo avresti fatto "domani"?
  • Dovevi scegliere tra test manuali o guardare l'ultimo episodio del tuo programma TV preferito (Teoria del Big Bang)?

La maggior parte delle volte, non succede nulla, e si sposta con successo il proprio codice in produzione senza problemi. Ma a volte, dopo essere passati alla produzione, tutto va storto. Sei bloccato a fissare un centinaio di buche in una nave che affonda, con più apparizioni ogni minuto. Tu fai non voglio trovarti in questa situazione.


Avvitarlo, spostalo nella produzione!
Immagine per gentile concessione di http://phenomenaonbreak.wordpress.com

TDD aveva lo scopo di eliminare le nostre scuse. Quando un programma è stato sviluppato utilizzando TDD, ci consente di apportare modifiche e test in modo rapido ed efficiente. Tutto quello che dobbiamo fare è eseguire i test automatici e voilà! Se passa tutti i test automatici, allora siamo a posto, se no, significa solo che abbiamo rotto qualcosa con le modifiche. Sapendo quali parti esatte del test hanno fallito, ci permette anche di individuare facilmente la parte delle modifiche che ha rotto, quindi rende più facile la correzione degli errori.


Sono venduto Come facciamo questo?

C'è una moltitudine di framework di test automatici di PHP che possiamo usare. Uno dei framework di test più diffusi è PHPUnit.

PHPUnit è un ottimo framework di testing, che può essere facilmente integrato nei tuoi progetti, o altri progetti costruiti su framework PHP popolari.

Per i nostri scopi, tuttavia, non avremo bisogno della moltitudine di funzioni offerte da PHPUnit. Invece, opteremo per creare i nostri test usando un framework di test molto più semplice, chiamato SimpleTest.

Nei prossimi passi, supponiamo che stiamo sviluppando un'applicazione guestbook in cui ogni utente può aggiungere e visualizzare le voci del guestbook. Supponiamo che il markup sia stato completato e che stiamo semplicemente facendo una classe che contiene il logica applicativa del guestbook, che è il punto in cui l'applicazione inserisce e legge nel database. La porzione di lettura di questa classe è ciò che svilupperemo e testeremo.


Passaggio 1. Configurare SimpleTest

Questo è senza dubbio il passo più facile di tutti. Anche questo ragazzo potrebbe farlo:


Posso farlo? Posso usare, mio, ehm? cervello!
Immagine per gentile concessione di http://longstreet.typepad.com/

Scarica SimpleTest qui, ed estrai in una cartella a tua scelta - preferibilmente la cartella in cui svilupperai il tuo codice, o il tuo PHP include_path per un facile accesso.

Per questo tutorial, ho impostato la cartella in questo modo:

Index.php eseguirà guestbook.php e invocherà il metodo di visualizzazione e visualizzerà le voci. All'interno della cartella classes è dove inseriremo la classe guestbook.php, e la cartella test è dove posizioniamo la libreria più semplice.


Passaggio 2. Pianifica il tuo attacco


Immagine per gentile concessione di http://connections.smsd.org/veterans

Il secondo passo, che è in realtà il più importante, è iniziare a creare i test. Per questo, hai davvero bisogno di pianificare e pensare a cosa farà la tua funzione, quali input possibili otterrà e gli output corrispondenti che invierà. Questo passaggio assomiglia a una partita a scacchi: devi sapere tutto sul tuo avversario (il programma), comprese tutte le sue debolezze (possibili errori) e punti di forza (cosa succede se funziona correttamente).

Quindi per la nostra applicazione guestbook, stabiliamo gli schemi:

vista

  • Questa funzione non avrà input poiché recupererà tutte le voci dal database e invierà i dati da stampare.
  • Restituirà una serie di record del guestbook, indicando il nome del poster e il suo messaggio. Se non ci sono record, dovrebbe comunque restituire un array vuoto.
  • Se ci sono record, la matrice avrà 1 o più valori in essa contenuti.
  • Allo stesso tempo, la matrice avrà una struttura specifica, qualcosa del tipo:
 Array ([0] => Array (['name'] = "Bob" ['message'] = "Ciao, sono Bob.") [1] => Array (['name'] = "Tom" ['message'] = "Ciao, sono Tom.")))

Passaggio 3. Scrivi un test!


Immagine per gentile concessione di http://cflhomeless.wordpress.com

Ora possiamo scrivere il nostro primo test. Iniziamo creando un file chiamato guestbook_test.php all'interno della cartella test.

  

Quindi, convertiamo ciò che abbiamo determinato dal passaggio due,.

 aggiungi ("Bob", "Ciao, sono Bob."); $ guestbook-> add ("Tom", "Ciao, sono Tom."); $ entries = $ guestbook-> viewAll (); $ count_is_greater_than_zero = (count ($ entries)> 0); $ This-> assertTrue ($ count_is_greater_than_zero); $ this-> assertIsA ($ entries, 'array'); foreach ($ entries come $ entry) $ this-> assertIsA ($ entry, 'array'); $ This-> assertTrue (isset ($ entry [ 'name'])); $ This-> assertTrue (isset ($ entry [ 'messaggio']));  function testViewGuestbookWithNoEntries () $ guestbook = new Guestbook (); $ Guestbook-> CancTutti (); // Elimina prima tutte le voci in modo che sappiamo che è una tabella vuota $ entries = $ guestbook-> viewAll (); $ this-> assertEqual ($ entries, array ()); 

Le asserzioni assicurano che una certa cosa sia ciò che dovrebbe essere, in sostanza, assicura che ciò che viene restituito è ciò che ti aspetti che ritorni. Per esempio, se una funzione dovrebbe restituire true se ha successo, allora nel nostro test, dovremmo affermare che il valore restituito è uguale a true.

Come puoi vedere qui, testiamo la visualizzazione del guestbook con le voci e senza. Controlliamo se questi due scenari superano i nostri criteri dal secondo passaggio. Probabilmente hai anche notato che ciascuna delle nostre funzioni di test inizia con la parola "test". L'abbiamo fatto perché, quando SimpleTest esegue questa classe, cercherà tutte le funzioni che iniziano con la parola 'test' ed eseguirla.

Nella nostra classe di test, abbiamo anche utilizzato alcuni metodi di asserzione, come assertTrue, assertIsA e assertEquals. La funzione assertTrue verifica se un valore è vero o no. AssertIsA controlla se una variabile è di un certo tipo o classe. Infine, assertEquals verifica se una variabile è totalmente uguale a un certo valore.

Esistono altri metodi di asserzione forniti da SimpleTest, che sono:

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 è la classe o il tipo $ t
assertNotA ($ x, $ t) Fallisce se $ x è della classe o del tipo $ t
assertEqual ($ x, $ y) Fallisce se $ x == $ y è falso
assertNotEqual ($ x, $ y) Fallisce se $ x == $ y è vero
assertWithinMargin ($ x, $ y, $ m) Fallire se abs ($ x - $ y) < $m is false
assertOutsideMargin ($ x, $ y, $ m) Fallire se abs ($ x - $ y) < $m is true
assertIdentical ($ x, $ y) Fallisce se $ x == $ y è falso o un tipo non corrispondente
assertNotIdentical ($ x, $ y) Fallisce se $ x == $ y è vero e i tipi corrispondono
assertReference ($ x, $ y) Fallire a meno che $ x e $ y siano la stessa variabile
assertClone ($ x, $ y) Fallire a meno che $ x e $ y siano copie identiche
assertPattern ($ p, $ x) Fallisci a meno che l'espressione regolare $ p corrisponda a $ x
assertNoPattern ($ p, $ x) Fallisce se l'espressione regolare $ p corrisponde a $ x
expectError ($ x) Ingoia qualsiasi errore di corrispondenza imminente
affermare ($ e) Errore nell'oggetto aspettativa fallita $ e

Elenco dei metodi di asserzione per gentile concessione di http://www.simpletest.org/en/unit_test_documentation.html


Passaggio 4. Non riuscire a vincere


Immagine per gentile concessione di http://verydemotivational.com

Una volta che hai finito di scrivere il codice, devi eseguire il test. La prima volta che esegui il test, DOVREBBE FALLISCI. Se non lo fa, vuol dire che il test in realtà non prova nulla.

Per eseguire il test, è sufficiente eseguire guestbook_test.php nel tuo browser. Dovresti vedere prima questo:

Ciò è accaduto perché non abbiamo ancora creato la nostra lezione per gli ospiti. Per fare ciò, crea guestbook.php all'interno della tua cartella delle classi. La classe dovrebbe contenere i metodi che intendiamo utilizzare, ma non dovrebbe contenere ancora nulla all'inizio. Ricorda, stiamo scrivendo prima i test prima scrivendo qualsiasi codice.

  

Quando si esegue nuovamente il test, dovrebbe essere simile a questo:

Come possiamo vedere qui, il nostro test ora sta vincendo fallendo. Ciò significa che il nostro test è ora pronto per ricevere "risposta".


Immagine gentilmente concessa da http://www.gamercastnetwork.com/forums

Passaggio 5. Rispondere al test scrivendo il codice


Ad un certo punto, ci siamo sentiti tutti così quando stiamo programmando.
Immagine per gentile concessione di http://fermentation.typepad.com/it/fermentation

Ora che abbiamo un test automatico funzionante, possiamo iniziare a scrivere codice. Apri il tuo guestbook.php classe e inizia a creare la risposta al tuo test.

  'Kirk', 'message' => 'Ciao, sono Kirk.' ), array ('name' => 'Ted', 'message' => 'Ciao, sono Ted.')); public function viewAll () // Qui, dovremmo recuperare tutti i record dal database. // Questo viene simulato restituendo l'array $ _entries return self :: $ _;  public function add ($ name, $ message) // Qui, simuliamo l'inserimento nel database aggiungendo un nuovo record nell'array $ _entries // Questo è il modo corretto di farlo: self :: $ _ entries [] = array ('nome' => $ nome, 'messaggio' => $ messaggio); self :: $ _ entries [] = array ('notname' => $ name, 'notmessage' => $ message); // oops, c'è un bug qui da qualche parte restituisce true;  public function deleteAll () // Abbiamo appena impostato la matrice $ _entries per simulare self :: $ _ entries = array (); ritorna vero; 

Questa classe guestbook.php ha alcuni bug in proposito, quindi possiamo vedere come appare se il nostro test fallisce.

Una volta eseguito il test, dovremmo vedere qualcosa di simile a questo:

L'output del test ci mostra in quale test e in quale asserzione il nostro codice ha fallito. Da questo, possiamo facilmente individuare che la riga 16 e 17 era l'affermazione che ha gettato l'errore.

 assertTrue (isset ($ entry [ 'name'])); $ This-> assertTrue (isset ($ entry [ 'messaggio'])) ;? 

Questo ci dice chiaramente che l'array di input restituito non aveva la chiave dell'array corretta. Sulla base di questo, sapremo facilmente quale parte del nostro codice è andata storta.

  $ nome, 'messaggio' => $ messaggio); //fisso! ritorna vero; ? 

Ora, quando eseguiremo di nuovo il nostro test, dovrebbe mostrarci:


Passaggio 6. Refactor e perfezionare il codice


Immagini per gentile concessione di http://www.osborneink.com e http://phuketnews.phuketindex.com

Dato che il codice che stiamo testando qui è piuttosto semplice, i nostri test e bug fixing non sono durati molto a lungo. Ma se questa fosse un'applicazione più complessa, dovresti apportare più modifiche al codice, renderlo più pulito, quindi è più facile da mantenere e molte altre cose. Il problema con questo, però, è che il cambiamento di solito introduce bug aggiuntivi. È qui che entra in gioco il nostro test automatizzato: una volta apportate le modifiche, possiamo semplicemente eseguire nuovamente il test. Se passa ancora, vuol dire che non abbiamo infranto nulla. Se fallisce, sappiamo che abbiamo commesso un errore. Ci informa anche dove si trova il problema e, si spera, in che modo riusciremo a risolverlo.


Passaggio 7. Risciacquare e ripetere


Immagine per gentile concessione di http://www.philstockworld.com

Alla fine, quando il tuo programma richiede nuove funzionalità, dovrai scrivere nuovi test. Questo è facile! Risciacquare e ripetere le procedure del passaggio 2 (poiché i file SimpleTest dovrebbero già essere impostati) e riavviare il ciclo da capo.


Conclusione

Esistono molto più approfonditi articoli di sviluppo basati sui test, e ancora più funzionalità di SimpleTest rispetto a ciò che è stato mostrato in questo articolo: cose come oggetti mock, stub, che rendono più facile la creazione di test. Se desideri saperne di più, la pagina di sviluppo guidata da test di Wikipedia dovrebbe metterti sulla giusta strada. Se si desidera utilizzare SimpleTest come framework di test, consultare la documentazione in linea e assicurarsi di rivedere le sue altre funzionalità.

I test sono parte integrante del ciclo di sviluppo, tuttavia, è troppo spesso la prima cosa da tagliare quando le scadenze sono imminenti. Spero che, dopo aver letto questo articolo, apprezzerai quanto sia utile investire nello sviluppo basato sui test.

Quali sono le tue opinioni su Test-Driven Development? È qualcosa che ti interessa implementare o pensi che sia una perdita di tempo? Fatemi sapere nei commenti!