Copertura del codice di prova dal mito alla realtà

C'è stato un tempo in cui i programmatori venivano pagati in base al numero di righe di codice che scrivevano. Sono stati trattati come codice sorgente producendo macchine che lavorano nei cubicoli e in cambio hanno considerato di programmare solo un lavoro che fanno otto ore al giorno e poi di dimenticarsene, per il resto della giornata.

Ma i tempi sono cambiati. La maggior parte dei luoghi di lavoro dei cubicoli è scomparsa e i programmatori hanno iniziato ad amare il loro mestiere. Con l'avvento delle tecniche Agile e del movimento Software Craftsmanship, sono emersi molti nuovi strumenti per aiutare il programmatore e il processo. Il TDD sta lentamente diventando il modo di scrivere codice e i segreti di SCRUM o Kanban sono stati rivelati anche ai programmatori negli angoli più bui del mondo dei cubicoli.

Test automatici e test driven development (TDD) sono alcune delle tecniche essenziali che Agile ha fornito a noi programmatori. E uno strumento che viene fornito con queste metodologie viene utilizzato per produrre la copertura del codice di test, che è l'argomento di questo articolo.

Definizione

"Nell'informatica, la copertura del codice è una misura usata per descrivere il grado in cui il codice sorgente di un programma è testato da una particolare suite di test." ~ Wikipedia

La definizione di cui sopra, tratta da Wikipedia, è uno dei modi più semplici per descrivere cosa significhi la copertura del codice. Fondamentalmente, nel tuo progetto hai un sacco di codice di produzione e un sacco di codice di test. Il codice di prova esercita il codice di produzione e la copertura del test indica quanto del codice di produzione è stato esercitato dai test.

Le informazioni possono essere presentate in vari modi, da semplici percentuali a una grafica piacevole o persino in tempo reale nel tuo IDE preferito.

Controlliamolo in azione

Useremo PHP come linguaggio per esemplificare il nostro codice. Inoltre, avremo bisogno di PHPUnit e XDebug per testare il nostro codice e raccogliere i dati di copertura.

Il codice sorgente

Ecco il codice sorgente che useremo. Puoi anche trovarlo nell'archivio allegato.

class WordWrap public function wrap ($ string = ", $ cols) $ string = trim ($ stringa); if (strlen ($ stringa)> $ cols) $ lastSpaceIndex = strrpos (substr ($ stringa, 0, $ cols), "); if ($ lastSpaceIndex! == false && substr ($ stringa, $ cols, 1)! = ") restituisce substr ($ stringa, 0, $ lastSpaceIndex)." \ n ". $ this-> wrap (substr ($ string, $ lastSpaceIndex), $ cols); else return substr ($ string, 0, $ cols). "\ n". $ this-> wrap (substr ($ stringa, $ cols), $ cols);  return $ string;

Il codice precedente contiene una semplice funzione che racchiude il testo in un numero specificato di caratteri, per riga.

Il codice di prova

Abbiamo scritto questo codice utilizzando Test Driven Development (TDD) e abbiamo una copertura del 100% del codice per questo. Ciò significa che eseguendo il nostro test, esercitiamo ogni singola riga del codice sorgente.

require_once __DIR__. '/ ... /WordWrap.php'; class WordWrapTest estende PHPUnit_Framework_TestCase function testItCanWrap () $ w = new WordWrap (); $ this-> assertEquals (", $ w-> wrap (null, 0)); $ this-> assertEquals (", $ w-> wrap (", 0)); $ this-> assertEquals ('a', $ w-> wrap ('a', 1)); $ this-> assertEquals ("a \ nb", $ w-> wrap ('a b', 1)); $ this-> assertEquals ("ab \ nc ", $ w-> wrap ('ab c', 3)); $ this-> assertEquals (" a \ nbc \ nd ", $ w-> wrap ('a bc d', 3));

Esecuzione dei test in CLI con copertura di solo testo

Un modo per ottenere i dati di copertura è eseguire i nostri test nella CLI (interfaccia della riga di comando) e analizzare l'output. Per questo esempio, assumeremo un sistema operativo simile a UNIX (Linux, MacOS, FreeBSD, ecc.). Gli utenti di Windows dovranno adattare leggermente i percorsi e i nomi dei file eseguibili, ma dovrebbe essere abbastanza simile.

Apriamo una console e cambiamo le directory nel tuo test cartella. Quindi corri PHPUnit con un'opzione per generare dati di copertura come testo normale.

phpunit --coverage-text =. / coverage.txt ./WordWrapTest.php

Questo dovrebbe funzionare fuori dalla scatola sulla maggior parte dei sistemi se XDebug è installato, tuttavia in alcuni casi, potresti incontrare un errore relativo ai fusi orari.

Avviso PHP: date (): non è sicuro affidarsi alle impostazioni del fuso orario del sistema. Sei * richiesto * per utilizzare l'impostazione date.timezone o la funzione date_default_timezone_set (). Nel caso in cui hai usato uno di questi metodi e ricevi ancora questo avviso, molto probabilmente hai sbagliato a digitare l'identificatore del fuso orario. Abbiamo selezionato il fuso orario 'UTC' per ora, ma per favore seleziona date.timezone per selezionare il tuo fuso orario. in phar: ///usr/share/php/phpunit/phpunit.phar/ PHP_CodeCoverage-1.2.10 / PHP / CodeCoverage / Report / Text.php sulla riga 124 

Questo può essere facilmente risolto specificando l'impostazione suggerita nel tuo php.ini file. Puoi trovare il modo di specificare il tuo fuso orario in questo elenco. Vengo dalla Romania, quindi userò la seguente impostazione:

date.timezone = Europa / Bucarest

Ora, se esegui il PHPUnit comando di nuovo, non dovresti vedere nessun messaggio di errore. Invece, verranno mostrati i risultati del test.

PHPUnit 3.7.20 di Sebastian Bergmann ... Tempo: 0 secondi, Memoria: 5.00Mb OK (2 test, 7 asserzioni) 

E i dati di copertura saranno nel file di testo specificato.

$ cat ./coverage.txt Rapporto sulla copertura del codice 2014-03-02 13:48:11 Riepilogo: Classi: 100,00% (1/1) Metodi: 100,00% (1/1) Righe: 2,68% (14/522) WordWrap Metodi: 100,00% (1/1) Righe: 100,00% (7/7) 

Analizziamolo un po '.

  • Classi: indica il numero di classi testate e il numero di classi coperte. WordWrap è la nostra unica lezione.
  • metodi: come con le classi. Abbiamo solo il nostro avvolgere () metodo, nient'altro.
  • Linee: come sopra, ma per linee di codice. Qui abbiamo un sacco di righe perché il riepilogo contiene tutte le linee di PHPUnit stesso.
  • Quindi abbiamo una sezione per ogni classe. Nel nostro caso, questo è solo WordWrap. Ogni sezione ha i propri metodi e dettagli di linea.

Sulla base di queste osservazioni, possiamo concludere che il nostro codice è coperto al 100% da test. Esattamente come ci aspettavamo prima di analizzare i dati di copertura.

Generazione dell'output della copertura HTML

Cambiando semplicemente un parametro semplice per PHPUnit, possiamo generare un buon output HTML.

$ mkdir ./coverage $ phpunit --coverage-html ./coverage ./WordWrapTest.php 

Se controlli il tuo ./copertura directory, troverai molti file lì. Non scriverò qui la lista perché è piuttosto estesa. Invece, ti mostrerò come appare in un browser web.

Questo è l'equivalente della sezione di riepilogo dalla versione di testo sopra. Siamo in grado di ingrandire seguendo i link proposti e vedere maggiori dettagli.

Copertura all'interno del nostro IDE

Gli esempi precedenti erano interessanti e sono abbastanza utili, Se il tuo codice è costruito su un server remoto a cui hai solo accesso a SHH o web. Ma non sarebbe bello avere tutte queste informazioni, vivere nel tuo IDE?

Se usi PHPStorm, tutto è a portata di click! Seleziona per eseguire i tuoi test con copertura e tutte le informazioni saranno semplicemente mostrate, magicamente.

Le informazioni sulla copertura saranno presenti nel tuo IDE, in diversi modi e in diversi punti:

  1. La percentuale di copertura del test verrà mostrata vicino a ciascuna directory e file.
  2. Nell'editor, durante la modifica del codice, a sinistra dei numeri di riga, un rettangolo verde o rosso segnerà ogni riga. Il verde rappresenta le linee testate, il rosso rappresenta quelle non testate. Le righe senza codice effettivo (righe vuote, solo parentesi graffe o parentesi, dichiarazioni di classi o metodi) non avranno alcun segno.
  3. Sul lato destro ci saranno i browser di file in cui è possibile sfogliare e ordinare rapidamente i file per copertura.
  4. Nell'output del test, vedrai una riga di testo che ti informa che la copertura del codice è stata generata.

The Myths About Code Coverage

Con uno strumento così potente nelle mani dello sviluppatore e sotto il naso della direzione, era inevitabile che alcuni miti emergessero. Dopo che i programmatori hanno rifiutato di essere pagati dal numero di righe di codice che scrivono, o i manager hanno capito quanto sia facile giocare al sistema, alcuni di loro hanno iniziato a pagare i programmatori in base alla percentuale di copertura del codice. Maggiore copertura del codice significa che il programmatore è stato più attento, giusto? È un mito. La copertura del codice non è una misura di quanto bene scrivi il codice.

A volte i programmatori tendono a pensare che il codice con una copertura del 100% non abbia bug. Un altro mito La copertura del codice ti dice semplicemente di aver testato ogni riga di codice. È una misura del numero di linee esercitate. Non è una misura del numero di linee correttamente implementate. Ad esempio, gli algoritmi scritti a metà con solo la metà dei test definiti avranno ancora una copertura del 100%. Questo non significa che l'algoritmo è finito o che funziona correttamente.

Infine, giocare al sistema è molto semplice. Naturalmente, se usi TDD, hai naturalmente un alto valore di copertura. Su interi progetti, il 100% è impossibile. Ma su piccoli moduli o classi, ottenere una copertura del 100% è molto semplice. Prendi ad esempio il nostro codice sorgente e immagina di non avere nessun test. Quale sarebbe la prova più semplice per esercitare tutto il codice?

function testItCanWrap () $ w = new WordWrap (); $ this-> assertEquals ("a b \ nc", $ w-> wrap ('a b c', 3)); $ this-> assertEquals ("a \ nbc \ nd", $ w-> wrap ('a bc d', 3)); 

Questo è tutto. Due asserzioni e copertura completa. Questo non è ciò che vogliamo. Questo test è così lontano da descrittivo e completo, che è ridicolo.

La realtà sulla copertura del codice

La copertura del codice è un indicatore di stato, non un'unità per misurare le prestazioni o la correttezza.

La copertura del codice è per i programmatori, non per i manager. È un modo per individuare problemi nel nostro codice. Un modo per trovare classi vecchie e non testate. Un modo per trovare percorsi non esercitati dai test che potrebbero portare a problemi.

Su progetti reali, la copertura del codice sarà sempre inferiore al 100%. Raggiungere una copertura perfetta non è possibile, o se lo è, è raramente un must. Tuttavia, per avere il 98% della copertura, è necessario raggiungere il 100%. Avere qualcos'altro come obiettivo non ha senso.

Ecco la copertura del codice sull'applicazione di configurazione di StorageOS di Syneto.

Il totale è solo del 35% circa, ma i risultati richiedono un'interpretazione. La maggior parte dei moduli è nel verde, con una copertura superiore al 70%. Tuttavia c'è una singola cartella, Vmware, che abbassa la media. È un modulo con molte classi contenenti solo definizioni per l'API di comunicazione. Non c'è motivo di testare quelle classi. Sono stati generati automaticamente dal codice attendibile. I programmatori lo sapranno e sapranno come interpretare i risultati. Un manager può insistere nel testarlo perché è una barra rossa e sembra sospetto per qualcuno che non conosce i dettagli interni del progetto. Avrebbe senso testarlo? Affatto! Sarebbe un test inutile, che richiederebbe decine di secondi preziosi di tempo di costruzione senza alcun vantaggio.

Pensieri finali

Ecco quindi dove ci troviamo con la copertura del codice: è un ottimo strumento per i programmatori, una fonte di informazioni per evidenziare possibili problemi, una realtà mal compresa per la maggior parte dei manager e un altro strumento per forzare e misurare le attività dei programmatori. Come con qualsiasi altro strumento, è uno che può essere usato e usato correttamente in modo corretto.