La copertura dei test è una misura adeguata della qualità del codice?


20

Se ho un codice che ha una copertura del test dell'80% (tutti i test superano), è giusto dire che è di qualità superiore rispetto al codice senza copertura del test?

O è giusto dire che è più mantenibile?


2
Una copertura del 100% non significa che sia stata ben testata. Ma lo 0% indica che non è stato testato affatto.
mouviciel,

1
Tecnicamente no. Praticamente si. L'esperienza ha insegnato a molti ingegneri e tester di software che quando la copertura del codice raggiunge circa l'80%, i tipi di difetti per i quali il test dell'unità è adeguato iniziano a stabilizzarsi. È il principio di pareto. Fondamentalmente una volta arrivato al punto in cui stai coprendo l'80% del codice, indipendentemente dalla qualità dei tuoi test, probabilmente hai testato il 20% del codice che causa la maggior parte dei potenziali problemi in modo abbastanza completo. Questa non è un'assoluta, ma piuttosto una saggezza convenzionale. Devi essere più accurato se le vite dipendono dai tuoi test.
Calphool,

@JoeRounceville Non sono sicuro ... Posso ottenere un'elevata copertura dei test mentre non collaudo nulla di veramente utile. La copertura indica semplicemente quanta parte del codice viene toccata dalla suite di test, non se i test sono significativi.
Andres F.,

1
@AndresF. Ecco perché ho detto "tecnicamente no, praticamente sì". Le persone non sono idioti (in genere). Non testano (in genere) solo casi senza sforzo. Quindi, in base all'esperienza , molti negozi si fermano da qualche parte intorno all'80% di copertura, facendo il presupposto (abbastanza sicuro) che la loro gente non sia idiota.
Calphool,

Risposte:


24

In senso stretto, non è corretto fare affermazioni fino a quando non viene stabilita la qualità della suite di test. Il superamento del 100% dei test non è significativo se la maggior parte dei test sono banali o ripetitivi tra loro.

La domanda è: nella storia del progetto, qualcuno di questi test ha scoperto bug? L'obiettivo di un test è trovare bug. E se non lo facessero, avrebbero fallito come test. Invece di migliorare la qualità del codice, potrebbero darti solo un falso senso di sicurezza.

Per migliorare i progetti di test, è possibile utilizzare (1) tecniche di whitebox, (2) tecniche di blackbox e (3) test di mutazione.

(1) Ecco alcune buone tecniche di whitebox da applicare ai progetti di test. Un test whitebox è costruito tenendo presente il codice sorgente specifico. Un aspetto importante del test della whitebox è la copertura del codice:

  • Viene chiamata ogni funzione? [Copertura funzionale]
  • Ogni affermazione viene eseguita? [Copertura delle dichiarazioni: sia la copertura funzionale sia la copertura delle dichiarazioni sono molto semplici, ma meglio di niente]
  • Per ogni decisione (come ifo while), hai un test che lo costringe a essere vero e altro che lo costringe a essere falso? [Copertura delle decisioni]
  • Per ogni condizione che è una congiunzione (usi &&) o disgiunzione (usi ||), ogni sottoespressione ha un test in cui è vero / falso? [Copertura condizioni]
  • Copertura del loop: hai un test che forza 0 iterazioni, 1 iterazione, 2 iterazioni?
  • Ognuno è breakcoperto da un anello?

(2) Le tecniche di Blackbox vengono utilizzate quando i requisiti sono disponibili, ma il codice stesso non lo è. Questi possono portare a test di alta qualità:

  • I tuoi test blackbox coprono più obiettivi di test? Vorrai che i tuoi test siano "grassi": non solo testano la funzione X, ma testano anche Y e Z. L'interazione di diverse funzionalità è un ottimo modo per trovare i bug.
  • L'unico caso in cui non si desidera test "grassi" è quando si verifica una condizione di errore. Ad esempio, test per input utente non valido. Se hai tentato di raggiungere più obiettivi di test di input non validi (ad esempio, un codice postale non valido e un indirizzo stradale non valido) è probabile che un caso stia mascherando l'altro.
  • Considerare i tipi di input e formare una "classe di equivalenza" per i tipi di input. Ad esempio, se il tuo codice verifica se un triangolo è equilatero, il test che utilizza un triangolo con lati (1, 1, 1) troverà probabilmente gli stessi tipi di errori dei dati del test (2, 2, 2) e (3, 3, 3) troveranno. È meglio passare il tempo a pensare ad altre classi di input. Ad esempio, se il tuo programma gestisce le tasse, ti consigliamo un test per ciascuna fascia d'imposta. [Questo si chiama partizionamento di equivalenza.]
  • Casi speciali sono spesso associati a difetti. I dati del test devono inoltre avere valori limite, come quelli sopra, sopra o sotto i bordi di un'attività di equivalenza. Ad esempio, nel test di un algoritmo di ordinamento, ti consigliamo di provare con un array vuoto, un array a singolo elemento, un array con due elementi e quindi un array molto grande. Dovresti considerare i casi limite non solo per l'input, ma anche per l'output. [Questa è chiamata analisi del valore limite.]
  • Un'altra tecnica è "Errore nell'ipotesi". Hai la sensazione se provi una combinazione speciale che puoi far decifrare il tuo programma? Allora provalo! Ricorda: il tuo obiettivo è trovare bug, non confermare che il programma sia valido . Alcune persone hanno il talento per indovinare l'errore.

(3) Infine, supponiamo che abbiate già molti bei test per la copertura del whitebox e le tecniche applicate per il blackbox. Cos'altro puoi fare? È tempo di testare i tuoi test . Una tecnica che puoi utilizzare è il test di mutazione.

Sotto test di mutazione, fai una modifica a (una copia di) il tuo programma, nella speranza di creare un bug. Una mutazione potrebbe essere:

Cambia un riferimento di una variabile in un'altra variabile; Inserisci la funzione abs (); Cambia da meno di a maggiore di; Elimina una dichiarazione; Sostituisci una variabile con una costante; Elimina un metodo di sostituzione; Elimina un riferimento a un metodo super; Cambia l'ordine degli argomenti

Crea diverse decine di mutanti, in vari punti del tuo programma [il programma dovrà ancora essere compilato per testare]. Se i tuoi test non trovano questi bug, ora devi scrivere un test in grado di trovare il bug nella versione mutata del tuo programma. Una volta che un test trova il bug, hai ucciso il mutante e puoi provarne un altro.


Addendum : ho dimenticato di menzionare questo effetto: i bug tendono a raggrupparsi . Ciò significa che più bug trovi in ​​un modulo, maggiore è la probabilità che troverai più bug. Quindi, se hai un test che fallisce (vale a dire, il test ha esito positivo, poiché l'obiettivo è trovare i bug), non solo dovresti correggere il bug, ma dovresti anche scrivere più test per il modulo, usando il tecniche sopra.

Finché trovi bug a un ritmo costante, gli sforzi di test devono continuare. Solo quando si verifica un calo del tasso di nuovi bug rilevati, si dovrebbe avere la certezza di aver compiuto buoni sforzi di test per quella fase di sviluppo.


7

Secondo una definizione è più mantenibile, poiché ogni cambiamento di rottura ha maggiori probabilità di essere colto dai test.

Tuttavia, il fatto che il codice superi i test unitari non significa che sia intrinsecamente di qualità superiore. Il codice potrebbe ancora essere formattato in modo errato con commenti irrilevanti e strutture dati inadeguate, ma può comunque superare i test.

So quale codice preferirei mantenere ed estendere.


7

Il codice senza test può essere di qualità estremamente elevata, leggibile, bello ed efficiente (o spazzatura totale), quindi no, non è corretto affermare che un codice con copertura del test dell'80% sia di qualità superiore rispetto al codice senza copertura del test.

Potrebbe essere corretto affermare che il codice coperto per l'80% con buoni test è probabilmente di qualità accettabile e probabilmente relativamente mantenibile. Ma garantisce poco, davvero.


3

Lo definirei più refattorabile. Il refactoring diventa estremamente semplice se il codice è coperto da molti test.

Sarebbe giusto chiamarlo più mantenibile.


2

Concordo sulla parte mantenibile. Michael Feathers ha recentemente pubblicato un video di un suo eccellente discorso chiamato " La profonda sinergia tra testabilità e buon design " in cui discute questo argomento. Nel discorso dice che la relazione è un modo, cioè il codice che è ben progettato è testabile, ma il codice verificabile non è necessariamente ben progettato.

Vale la pena notare che lo streaming video non è eccezionale nel video, quindi potrebbe valere la pena scaricarlo se si desidera guardarlo per intero.


-2

Mi pongo questa domanda da qualche tempo in relazione alla "copertura delle condizioni". Che ne dici di questa pagina di atollic.com "Perché l'analisi della copertura del codice?"

Più tecnicamente, l'analisi della copertura del codice trova aree del programma che non sono coperte dai casi di test, consentendo di creare test aggiuntivi che coprono parti del programma altrimenti non testate. È quindi importante comprendere che la copertura del codice consente di comprendere la qualità delle procedure di test, non la qualità del codice stesso .

Questo sembra essere abbastanza rilevante qui. Se hai un set di casi di test che riesce a raggiungere un certo livello di (codice o altro) di copertura, molto probabilmente stai invocando il codice in prova con un insieme piuttosto esaustivo di valori di input! Questo non ti dirà molto sul codice in prova (a meno che il codice non esploda o generi errori rilevabili) ma ti dia fiducia nel set di casi di test .

In un'interessante modifica del Necker Cube , il codice di test è ora in fase di test da parte del codice in prova!


-3

Esistono molti modi per garantire che un programma faccia ciò che si intende e per garantire che le modifiche non abbiano effetti indesiderati.

Il test è uno. Evitare la mutazione dei dati è un altro. Quindi è un sistema di tipi. O verifica formale.

Pertanto, anche se concordo sul fatto che i test sono generalmente positivi, una determinata percentuale di test potrebbe non significare molto. Preferirei fare affidamento su qualcosa scritto in Haskell senza test piuttosto che su una libreria PHP ben collaudata


questa è solo la tua opinione o puoi sostenerla in qualche modo?
moscerino il

2
Il test non è un modo per garantire che un programma faccia ciò che si intende.
Andres F.

1
Poi mi chiedo quali siano i test
Andrea,

@gnat questa è ovviamente la mia opinione. Tuttavia, dice ciò che viene detto. Ho preso Haskell come esempio di linguaggio il cui compilatore è molto rigoroso e offre molte garanzie circa la buona formazione degli input, i tipi, gli effetti collaterali, la mutazione dei dati. Ho preso PHP come esempio di un linguaggio il cui interprete è molto indulgente e che non ha nemmeno una specifica. Anche in assenza di test, la presenza di tutte le garanzie dei tipi e del sistema di effetti generalmente fornisce un discreto grado di affidabilità. Per compensare ciò con i test, si dovrebbe avere una suite molto completa
Andrea,

Forse ero un po 'affrettato quando ho scritto - ero al telefono - ma penso ancora che ci sia un punto. Non voglio basarmi su PHP, ma penso che affermare che a confronto Haskell offra un grado di affidabilità molto maggiore sia una dichiarazione oggettiva
Andrea
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.