Come si controlla che il codice sia stato coperto automaticamente?

6

Sono in procinto di configurare un server Bamboo per alcuni nuovi progetti per un push su TDD in un flusso di lavoro CI / CD. Certo, il collaudo delle unità è ottimo, ma solo come log com'è.

Ora questo potrebbe essere meglio in un hook pre-ricezione GIT su alcuni rami (cioè: sviluppo e rami di rilascio principali) ma come dovrebbe essere applicata la copertura del codice, se non del tutto. Sono felice di avere fiducia nei committer per garantire che il codice sia coperto, ma in che modo queste cose vengono mantenute senza alcun slittamento oltre alla diligenza e alla coerenza?

In breve, vorrei vedere come gli altri applicano la copertura del test come un processo automatico durante le fasi di commit o di costruzione.

    
posta Daniel Park 10.02.2015 - 23:43
fonte

7 risposte

15

Non devi applicare automaticamente la copertura del codice.

Questo è come imporre le linee massime di codice per metodo: concordato, la maggior parte dei metodi dovrebbe essere inferiore a, diciamo, 20 LOC, ma ci sono casi validi in cui i metodi sarebbero più lunghi di quello.

Allo stesso modo, il targeting di una data percentuale di copertura del codice per classe può portare a conseguenze indesiderate. Ad esempio:

  • Le classi di codici o le classi di codice delle piastre di caldaia create dai generatori di codice potrebbero non essere affatto testate. Costringere gli sviluppatori a testarli non avrà alcun vantaggio e avrà un costo notevole in termini di tempo impiegato a farlo.

  • La gestione semplice del codice di parti non importanti dell'applicazione non deve necessariamente essere testata.

  • In alcune lingue, alcuni codici non possono essere testati. Ho avuto questo caso in C # con metodi anonimi su una libreria in cui ho voluto per avere una copertura del 100% del codice. Questi casi potrebbero essere demoralizzanti per gli sviluppatori.

Ancora più importante, la copertura del codice dovrebbe essere proporzionale a due aspetti del codice: quanto è critico e quanto è complicato :

  • Un pezzo di codice con una logica complicata che fa parte delle funzionalità principali di un'applicazione dovrebbe essere esaminato attentamente, perché errori o regressioni possono avere conseguenze importanti.

  • Un semplice pezzo di codice che gestisce una caratteristica che nessuno usa potrebbe avere test di base che coprono solo casi di base.

Ovviamente, puoi utilizzare la copertura del codice come misura , in particolare per confrontare il modo in cui team diversi ottengono la copertura del codice: potrebbero esserci team meno disciplinati e più riluttanti quando si tratta di test. In questi casi, potresti voler combinare questa metrica con altre, come il numero di bug, il tempo impiegato a risolvere bug o il numero di commenti durante le revisioni del codice.

Potresti anche voler applicare almeno alcuni coverage di codice, ad esempio il 60% ¹, su singoli progetti dove ha senso (fai attenzione a escludere prototipi, codice generato , CRUD, ecc.) Rendere possibile agli sviluppatori di contrassegnare classi specifiche come escluse dalla copertura del codice è anche bello². In questo caso, questo può essere fatto sotto forma di un assegno che fallisce una compilazione se la copertura del codice è inferiore al minimo richiesto. Vorrei farlo nella fase di costruzione, non nella fase di commit , poiché non è previsto l'esecuzione di test unitari durante il commit .

¹ Considererei il 60% come minimo ragionevole basato sulla mia base di codice: quasi ogni progetto o classe che ha meno del 60% di copertura del codice è veramente non testato . Questo può variare molto da una lingua all'altra e da una società all'altra (in alcune aziende lo 0% è uno standard). Assicurati di discutere con il tuo team cosa è normale e cosa è troppo alto per loro. Forse stanno colpendo costantemente il 95% e possono facilmente raggiungere il 99%, o forse hanno difficoltà ad aumentare la copertura del codice dal 70% al 75%.

² Dato che l'eventuale abuso verrà rilevato durante le revisioni del codice, non dovresti aver paura di dare questa possibilità agli sviluppatori. Questo è simile alla possibilità di escludere alcune parti del codice dai controlli dei linters o dei controllori di stile. JSLint, StyleCop e Code Analysis sono tre esempi in cui l'esclusione è supportata ed è effettivamente utile senza incoraggiare l'abuso.

    
risposta data 11.02.2015 - 00:22
fonte
4

Considera il seguente codice:

rsq = a*a + b*b;
if (rsq >= 0.0) {
    r = sqrt(rsq);
}
else {
    handle_this_impossible_event();
}

Non c'è modo di creare un caso di test che raggiungerà quel altro ramo. Tuttavia, se si trattasse di un software di volo critico per la sicurezza, le persone si troverebbero ovunque nel caso dell'autore se quella protezione contro l'invio di un valore negativo a sqrt non fosse presente. In genere il calcolo di rsq = a*a + b*b e l'estrazione della radice quadrata sono separati da più righe di codice. Nel frattempo, un raggio cosmico può capovolgere il bit del segno su rsq .

In effetti, il software di volo ha richiamato l'equivalente di handle_this_impossible_event() , più volte. Di solito questo comporta il passaggio al controllo su un computer ridondante, con la chiusura forzata del computer sospetto, il riavvio del computer sospetto e infine il computer sospetto che assume il ruolo di backup. È molto meglio del computer di volo principale che sta andando su whacko.

Anche nel software di volo, è impossibile ottenere una copertura del 100% del codice. Le persone che affermano di aver ottenuto questo o hanno un codice banale o non hanno abbastanza test contro questi eventi impossibili.

    
risposta data 11.02.2015 - 05:52
fonte
4

La copertura del test è una metrica utile per la salute generale del tuo progetto. Una copertura di test elevata consente di prendere una decisione informata sul fatto che il software funzionerà come previsto al momento dell'implementazione; con una bassa copertura di test che implica che stai semplicemente indovinando. Esistono strumenti per misurare la copertura automaticamente, questi di solito funzionano eseguendo il programma in un contesto di debug o iniettando le operazioni contabili nel codice eseguito.

Esistono diversi tipi di test e diversi tipi di metriche di copertura. Le metriche di copertura comuni comprendono copertura delle funzioni, copertura delle dichiarazioni, copertura delle filiali e copertura delle condizioni, sebbene ci siano altre .

  • I test unitari verificano se l'implementazione di un'unità concettuale (modulo, classe, metodo, ...) è conforme alle sue specifiche (in TDD, il test è la specifica). Le unità prive del proprio test di unità sono una bandiera rossa, anche se potrebbero essere coperte da test in stile integrazione.

    I test unitari dovrebbero implicare una copertura quasi totale della funzione - poiché il test unitario esercita l'intera interfaccia pubblica di tale unità, non ci dovrebbe essere alcuna funzionalità che non viene toccata da questi test. Se stai introducendo il test dell'unità su una base di codice esistente, la copertura delle funzioni è un indicatore di progresso approssimativo.

    Un test unitario dovrebbe cercare di ottenere una buona copertura informativa (75% -100%). La copertura delle dichiarazioni è una metrica di qualità per un test unitario. La copertura totale non è sempre possibile e probabilmente utilizzi meglio il tuo tempo piuttosto che migliorare la copertura oltre il 95%.

    La copertura delle filiali e delle condizioni è più complicata. Quanto più un codice è complicato o importante, tanto maggiori dovrebbero essere queste metriche. Ma per un codice non spettacolare, la copertura delle dichiarazioni elevate tende ad essere sufficiente (e implica già una copertura delle filiali di almeno il 50%). Guardare il report sulla copertura delle condizioni di una unità può aiutare a costruire casi di test migliori.

  • I test di integrazione controllano se più unità possono funzionare correttamente l'una con l'altra. I test di integrazione possono essere molto utili senza ottenere punteggi elevati in qualsiasi metrica di copertura. Mentre i test di integrazione di solito esercitano una gran parte delle interfacce della loro unità (cioè hanno una copertura di funzione elevata), gli interni di queste unità sono già stati coperti dai test unitari.

Esecuzione di test prima che il codice venga unito in un ramo principale è una buona idea. Tuttavia, calcolare le metriche di copertura del test per l'intero programma tende a richiedere molto tempo: questo è un buon lavoro per una build notturna. Se riesci a capire come fare, un buon compromesso sarebbe di eseguire solo test modificati o unit test su unità modificate in un hook Git. I fallimenti dei test non sono accettabili per qualcosa di diverso da "work in progress" -commits. Se le metriche di copertura selezionate scendono al di sotto di qualche soglia (ad esempio copertura dell'istruzione inferiore all'80% o introduzione di nuovi metodi senza test corrispondenti), questi problemi dovrebbero essere trattati come un avvertimento, con l'opportunità per lo sviluppatore di risolvere questi potenziali problemi. Tuttavia, a volte ci sono buone ragioni per ignorare questi avvertimenti e gli sviluppatori dovrebbero poterlo fare.

Il test è buono, ma troppo può diventare fastidioso. Un feedback rapido e pertinente può aiutare a promuovere l'attenzione sulla qualità, ma non vuoi che interferisca con la produzione di valore. Personalmente preferisco eseguire i test manualmente, poiché mi consente di ottenere un feedback più rapido sulla parte su cui sto lavorando. Prima del rilascio, farò un focus di qualità in cui utilizzo analisi statiche, profiler e strumenti di copertura del codice per trovare le zone problematiche (alcuni di questi passaggi fanno parte di una suite di test pre-release).

    
risposta data 11.02.2015 - 13:02
fonte
3

Nessuno ha menzionato test di mutazione . L'idea dietro di loro è abbastanza pratica e intuitiva.

Funzionano modificando casualmente il codice sorgente (ad esempio cambiando ">" in "<") - quindi la mutazione - e controllando se queste modifiche casuali interrompono qualsiasi test.

Se non lo fanno, allora a) il codice in questione potrebbe non essere necessario, oppure b) (più probabilmente) questa parte di codice non è coperta da un test, poiché la sua interruzione non viene rilevata.

    
risposta data 11.02.2015 - 12:55
fonte
1

Ovviamente i dati sulla copertura del codice possono essere ottenuti automaticamente, ma non dovrebbero essere prese decisioni automatiche basate su di essi, per ragioni che altri hanno già spiegato. (Troppo sfocato, troppo margine di errore.)

Tuttavia, la cosa migliore da fare è avere un processo stabilito in base al quale lo stato attuale del progetto in termini di copertura del codice viene regolarmente controllato dagli esseri umani, possibilmente con rapporti giornalieri che arrivano nella casella di posta del responsabile del progetto.

Negli ambienti aziendali questo si ottiene con gli strumenti Continuous Integration come Hudson, Jenkins, ecc. Questi strumenti sono configurati per controllare regolarmente l'intero progetto dal repository del codice sorgente, crearlo, eseguire il prova e genera report. Ovviamente, possono essere configurati per eseguire i test in modalità di copertura del codice e includere i risultati in questi rapporti.

Jetbrains rende anche TeamCity, che mi sembra un po 'più leggero e adatto a negozi di software più piccoli.

Quindi, il project manager riceve periodicamente rapporti sulla copertura del codice, usa il suo buon giudizio e agisce da esecutore se necessario.

    
risposta data 11.02.2015 - 11:00
fonte
0

La copertura del codice può essere controllata automaticamente, nonostante l'opinione popolare, la suite di strumenti di Rational Purify includeva una funzionalità di copertura del codice. Si basava sulla strumentazione di tutte le funzioni (funzionava sui binari, aggiornava ogni funzione o chiamava con un po 'di codice in più) in modo che potesse scrivere i dati che venivano poi mostrati all'utente. Tecnologia piuttosto interessante, soprattutto in quel momento.

Tuttavia, anche quando abbiamo provato davvero, davvero difficile ottenere una copertura del 100%, siamo riusciti a gestire solo il 70% circa! Quindi è un esercizio un po 'inutile.

Tuttavia, nella situazione in cui si scrivono i test delle unità, penso che la copertura del 100% dei test unitari sia ancora più inutile. Unit test quei metodi che richiedono test unitari, non tutti i getter o setter! Il test delle unità dovrebbe riguardare la verifica delle funzioni (o classi TBH) e non cercare di spuntare le caselle in qualche processo o strumento che mostra dei buoni segni di spunta verde.

    
risposta data 11.02.2015 - 11:21
fonte
0

Ho creato uno strumento per questo

link

Utilizzo

bin/diffFilter --phpunit diff.txt clover.xml 70

Fallirà se meno del 70% del diff è coperto da test unitari.

Ottieni il diff di.

git diff origin/master... > diff.txt

Supponendo che tu abbia diramato il master e si unirà di nuovo nel master

Ignora il flag phpunit nel codice, in realtà è solo un controllo del trifoglio, quindi tutto ciò che può essere emesso dal trifoglio può usarlo.

Come altre risposte suggerite, mettere questo al 100% non è una buona idea

    
risposta data 18.08.2017 - 22:34
fonte

Leggi altre domande sui tag