Determinare cos'è un utile test unitario


47

Ho esaminato i documenti di phpunit e sono arrivato attraverso la seguente citazione:

Puoi sempre scrivere più test. Tuttavia, scoprirai rapidamente che solo una parte dei test che puoi immaginare sono effettivamente utili. Quello che vuoi è scrivere test che falliscono anche se pensi che dovrebbero funzionare, o test che riescono anche se pensi che dovrebbero fallire. Un altro modo di pensarci è in termini di costi / benefici. Vuoi scrivere dei test che ti ripagheranno con le informazioni. --Erich Gamma

Mi ha fatto pensare. Come si determina ciò che rende un'unità di test più utile di un'altra, oltre a quanto indicato in quella citazione su costi / benefici. Come fai a decidere quale parte del tuo codice crei unit test? Lo sto chiedendo perché un'altra di quelle citazioni diceva anche:

Quindi se non si tratta di test, di cosa si tratta? Si tratta di capire cosa stai cercando di fare prima di scappare mezzo armato per provare a farlo. Scrivi una specifica che inchioda un piccolo aspetto del comportamento in una forma concisa, inequivocabile ed eseguibile. È così semplice. Significa che scrivi dei test? No. Significa che scrivi le specifiche di ciò che il tuo codice dovrà fare. Significa che devi specificare il comportamento del tuo codice in anticipo. Ma non molto tempo prima. In effetti, proprio prima di scrivere il codice è meglio perché è quando hai tutte le informazioni a portata di mano che vuoi fino a quel momento. Come il TDD ben fatto, lavori con piccoli incrementi ... specificando un piccolo aspetto del comportamento alla volta, quindi implementandolo. Quando ti rendi conto che si tratta solo di specificare il comportamento e non di scrivere test, il tuo punto di vista cambia. Improvvisamente l'idea di avere una classe Test per ciascuna delle vostre classi di produzione è ridicolmente limitante. E il pensiero di testare ciascuno dei tuoi metodi con il proprio metodo di prova (in una relazione 1-1) sarà ridicolo. --Dave Astels

La sezione importante è quella

* E l'idea di testare ciascuno dei tuoi metodi con il proprio metodo di prova (in una relazione 1-1) sarà ridicola. *

Quindi, se la creazione di un test per ciascun metodo è "ridicola", come / quando hai scelto ciò per cui scrivi i test?


5
È una buona domanda, ma penso che sia una domanda indipendente dal linguaggio di programmazione - perché l'hai taggata php?

Ecco alcuni articoli davvero validi che mi hanno fornito una buona direzione su quali unit test dovrei scrivere e su quali aree sono importanti per fornire test automatizzati. - blog.stevensanderson.com/2009/11/04/… - blog.stevensanderson.com/2009/08/24/… - ayende.com/blog/4218/scenario-driven-tests
Kane

Risposte:


27

Quanti test per metodo?

Bene, il massimo teorico e altamente impraticabile è la complessità del N-Path (supponiamo che i test coprano tutti modi diversi attraverso il codice;)). Il minimo è UNO !. In base al metodo pubblico , cioè, non verifica i dettagli dell'implementazione, ma solo comportamenti esterni di una classe (restituisce valori e chiama altri oggetti).

Citi:

* E l'idea di testare ciascuno dei tuoi metodi con il proprio metodo di prova (in una relazione 1-1) sarà ridicola. *

e poi chiedi:

Quindi, se la creazione di un test per ciascun metodo è "ridicola", come / quando hai scelto ciò per cui scrivi i test?

Ma penso che tu abbia frainteso l'autore qui:

L'idea di avere one test methodper one method in the class to testè ciò che l'autore chiama "risibile".

(Almeno per me) Non si tratta di "meno", si tratta di "più"

Quindi lasciami riformulare come l'ho capito:

E l'idea di testare ciascuno dei tuoi metodi con UN SOLO METODO (il suo metodo di prova in una relazione 1-1) sarà ridicola.

Per citare di nuovo il tuo preventivo:

Quando ti rendi conto che si tratta solo di specificare il comportamento e non di scrivere test, il tuo punto di vista cambia.


Quando pratichi TDD non pensi :

Ho un metodo calculateX($a, $b);e ha bisogno di un test testCalculcateXche testa TUTTO sul metodo.

Quello che TDD ti dice è pensare a come il tuo codice DOVREBBE FARE :

Ho bisogno di calcolare il più grande dei due valori ( primo caso di test! ) Ma se $ a è inferiore a zero, allora dovrebbe produrre un errore ( secondo caso di test! ) E se $ b è inferiore a zero dovrebbe .... ( terzo caso di test! ) e così via.


Vuoi testare comportamenti, non solo singoli metodi senza contesto.

In questo modo ottieni una suite di test che è documentazione per il tuo codice e VERAMENTE spiega cosa dovrebbe fare, forse anche perché :)


Come fai a decidere quale parte del tuo codice crei unit test?

Bene, tutto ciò che finisce nel repository o ovunque vicino alla produzione ha bisogno di un test. Non penso che l'autore delle tue citazioni non sarebbe d'accordo con quello che ho cercato di affermare in quanto sopra.

Se non si dispone di un test, diventa molto più difficile (più costoso) cambiare il codice, soprattutto se non si sta effettuando la modifica.

TDD è un modo per assicurarti di avere test per TUTTO ma finché SCRIVI i test va bene. Solitamente scriverli nello stesso giorno aiuta perché non lo farai più tardi, vero? :)



Risposta ai commenti:

una discreta quantità di metodi non può essere testata in un particolare contesto perché dipendono o dipendono da altri metodi

Bene, ci sono tre cose che quei metodi possono chiamare:

Metodi pubblici di altre classi

Possiamo deridere altre classi, quindi abbiamo definito lo stato lì. Abbiamo il controllo del contesto, quindi non è un problema.

* Metodi protetti o privati ​​sullo stesso *

Tutto ciò che non fa parte dell'API pubblica di una classe non viene testato direttamente, di solito.

Volete testare il comportamento e non l'implementazione e se una classe fa tutto ciò che funziona in un unico grande metodo pubblico o in molti metodi protetti più piccoli che vengono chiamati è l' implementazione . Vuoi essere in grado di CAMBIARE quei metodi protetti SENZA toccare i tuoi test. Perché i tuoi test falliranno se il tuo codice cambia comportamento! Ecco a cosa servono i tuoi test, per dirti quando rompi qualcosa :)

Metodi pubblici sulla stessa classe

Non succede molto spesso? E se nell'esempio seguente piace, ci sono alcuni modi per gestirlo:

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

Che i setter esistano e non facciano parte della firma del metodo execute è un altro argomento;)

Ciò che possiamo testare qui è se l'esecuzione viene fatta esplodere quando impostiamo valori errati. Ciò setBlagenera un'eccezione quando si passa una stringa può essere testato separatamente, ma se vogliamo verificare che quei due valori consentiti (12 e 14) non funzionino INSIEME (per qualsiasi motivo) di quello è un caso di test.

Se vuoi una "buona" suite di test puoi, in php, forse (!) Aggiungere @covers Stuff::executeun'annotazione per assicurarti di generare solo la copertura del codice per questo metodo e le altre cose che sono appena configurate devono essere testate separatamente (di nuovo, se lo vuoi).

Quindi il punto è: forse devi prima creare un po 'del mondo circostante, ma dovresti essere in grado di scrivere casi di test significativi che di solito abbracciano solo una o forse due funzioni reali (i setter non contano qui). Il resto può essere deriso dall'etere o essere testato prima e poi fatto affidamento (vedi @depends)


* Nota: la domanda è stata migrata da SO e inizialmente riguardava PHP / PHPUnit, ecco perché il codice di esempio e i riferimenti provengono dal mondo php, penso che questo sia applicabile anche ad altre lingue poiché phpunit non differisce molto da altre xUnit framework di test.


Molto dettagliato e informativo ... Hai detto "Vuoi testare comportamenti, non solo singoli metodi senza contesto.", Sicuramente una discreta quantità di metodi non può essere testata in un particolare contesto perché dipendono o dipendono da altri metodi , quindi una condizione di test utile sarebbe contestuale solo se anche le persone a carico fossero sottoposte a test? O sto interpretando male cosa intendi>
zcourts,

@ robinsonc494
Modificherò

grazie per le modifiche e l'esempio, sicuramente aiuta. Penso che la mia confusione (se così si può chiamare) era che, anche se ho letto dei test per il "comportamento", in qualche modo ho pensato intrinsecamente (forse?) A casi di test incentrati sull'implementazione.
zcourts,

@ robinsonc494 Forse pensaci in questo modo: se dai un pugno a qualcuno, etere prende a pugni, chiama la polizia o scappa. Quel comportamento. È quello che fa la persona. Il fatto che usi le sue cozze innescate da piccole cariche elettriche dal suo cervello è implementazione. Se vuoi testare la reazione di qualcuno, dai un pugno e vedi se si comporta come te lo aspetti. Non lo metti in uno scanner per il cervello e vedi se gli impulsi vengono inviati alle cozze. Alcuni vanno per le lezioni praticamente;)
edorian

3
Penso che il miglior esempio che ho visto di TDD che mi abbia davvero aiutato a fare clic su come scrivere i casi di test sia stato il Bowling Game Kata di zio Bob Martin. slideshare.net/lalitkale/bowling-game-kata-by-robert-c-martin
Amy Anuszewski

2

Test e unit test non sono le stesse cose. Unit Testing è un sottoinsieme molto importante e interessante di Test in generale. Direi che il focus di Unit Testing ci porta a pensare a questo tipo di test in modi che in qualche modo contraddicono le citazioni sopra.

In primo luogo, se seguiamo TDD o persino DTH (ovvero sviluppiamo e testiamo in stretta armonia) stiamo usando i test che scriviamo come focus per ottenere il nostro design giusto. Pensando ai casi angolari e scrivendo i test di conseguenza, evitiamo che i bug entrino in primo luogo, quindi in realtà scriviamo test che prevediamo di superare (OK proprio all'inizio del TDD falliscono, ma questo è solo un artefatto di ordinazione, quando il codice è finito, ci aspettiamo che passino, e molti lo fanno, perché hai pensato al codice.

Secondo, i test unitari si realizzano davvero quando rifattorizziamo. Modifichiamo la nostra implementazione ma ci aspettiamo che le risposte rimangano invariate: il Test unitario è la nostra protezione contro la rottura del nostro contratto di interfaccia. Quindi uno di nuovo ci aspettiamo che i test passino.

Ciò implica che per la nostra interfaccia pubblica, che presumibilmente è stabile, abbiamo bisogno di una tracciabilità chiara in modo da poter vedere che ogni metodo pubblico è testato.

Per rispondere alla tua domanda esplicita: Unit Test per l'interfaccia pubblica ha valore.

modificato nel commento di risposta:

Test di metodi privati? Sì, dovremmo, ma se non dovessimo testare qualcosa, allora è lì che scenderei a compromessi. Dopotutto, se i metodi pubblici funzionano, allora quei bug nelle cose private possono essere così importanti? Pragmaticamente, l'abbandono tende a succedere nelle cose private, tu lavori duramente per mantenere la tua interfaccia pubblica, ma se le cose da cui dipendi cambiano le cose private possono cambiare. Ad un certo punto potremmo scoprire che mantenere i test interni è un grande sforzo. Questo sforzo è ben speso?


Quindi, solo per essere sicuro di capire cosa stai dicendo: Durante il test, concentrati sul test dell'interfaccia pubblica, giusto? Supponendo che sia corretto ... non c'è una maggiore possibilità di lasciare bug in metodi / interfacce private per le quali non hai fatto test unitari, alcuni bug insidiosi nell'interfaccia "privata" non testata potrebbero portare a un test che passa quando avrebbe dovuto fallire davvero. Sbaglio nel pensare così?
zcourts,

Usando la copertura del codice puoi capire quando il codice nei tuoi metodi privati ​​non viene eseguito durante il test dei tuoi metodi pubblici. Se i tuoi metodi pubblici sono completamente coperti, tutti i metodi privati ​​scoperti sono ovviamente inutilizzati e possono essere rimossi. In caso contrario, sono necessari ulteriori test per i metodi pubblici.
David Harkness,

2

I test unitari dovrebbero far parte di una strategia di test più ampia. Seguo questi principi nella scelta dei tipi di test da scrivere e quando:

  • Concentrati sulla scrittura di test end-to-end. Copri più codice per test che con i test unitari e quindi ottieni più test bang per il dollaro. Rendi queste la validazione automatizzata pane e burro del tuo sistema nel suo insieme.

  • Scorri verso il basso per scrivere unit test attorno a pepite di logica complicata. I test unitari valgono il loro peso in situazioni in cui i test end-to-end sarebbero difficili da eseguire il debug o difficili da scrivere per un'adeguata copertura del codice.

  • Attendere che l'API su cui si sta eseguendo il test sia stabile per scrivere entrambi i tipi di test. Vuoi evitare di dover riformattare sia la tua implementazione che i tuoi test.

Rob Ashton ha un bell'articolo su questo argomento, che ho attinto pesantemente per articolare i principi di cui sopra.


+1 - Non necessariamente sono d'accordo con tutti i tuoi punti (o punti dell'articolo) ma dove sono d'accordo è che la maggior parte dei test unitari sono inutili se eseguiti alla cieca (approccio TDD). Tuttavia, sono estremamente utili se sei intelligente nel decidere cosa è degno di passare del tempo a fare unit test. Concordo pienamente sul fatto che tu guadagni molto, molto di più per lo stipendio scrivendo test di livello superiore, in particolare test automatizzati a livello di sottosistema. Il problema con i test automatici end-to-end è che sarebbero difficili, se non del tutto impraticabili, per un sistema se avesse dimensioni / complessità.
Dunk il

0

Tendo a seguire un approccio diverso ai test unitari che sembra funzionare bene. Invece di pensare a un unit test come a "Testare un comportamento", lo considero più come "una specifica che il mio codice deve seguire". In questo modo, puoi sostanzialmente dichiarare che un oggetto dovrebbe comportarsi in un certo modo, e dato che supponi che altrove nel tuo programma, puoi essere abbastanza certo che è relativamente privo di bug.

Se stai scrivendo un'API pubblica, questo è estremamente prezioso. Tuttavia, avrai sempre bisogno di una buona dose di test di integrazione end-to-end, anche perché avvicinarsi al 100% della copertura dei test unitari di solito non ne vale la pena e mancheranno cose che la maggior parte considererebbe "non verificabili" con metodi di test unitari (derisione, eccetera)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.