Spiegazione della copertura del codice: copertura di linea o di filiale e cosa significa realmente il 100%.

  1. Home
  2. Blog
  3. Test del software

Test del software

Spiegazione della copertura del codice: Copertura di linea o di filiale e cosa significa realmente 100%

La copertura ti dice quale codice sono stati eseguiti i tuoi test. Non ti dice se i test hanno verificato qualcosa. Abbiamo misurato un piccolo modulo in tre modi per mostrare la differenza.

8 min di lettura
Spiegazione della copertura del codice: copertura di linea o di filiale e cosa significa veramente 100%.

Punti chiave

  • La copertura del codice misura la quantità di codice eseguito durante i test.
  • La copertura delle filiali è più rigorosa della copertura della linea e più utile.
  • Nel nostro test, una suite senza nessuna asserzione ha comunque raggiunto una copertura del 100%.
  • Utilizza la copertura per trovare codice non testato, non come prova di qualità.

Un team segnala con orgoglio una copertura del codice del 95%, quindi invia un bug in una riga che è stata “coperta”. Questa non è una contraddizione. La copertura risponde a una domanda ristretta: i test hanno eseguito questo codice? Se abbiano controllato il risultato è una questione completamente diversa.

Abbiamo utilizzato Python con cover.py 7.17 e pytest 9.2. Ogni numero e rapporto riportato di seguito proviene dalla nostra corsa. Le idee si applicano in qualsiasi lingua.

Cos’è la copertura del codice?

La copertura del codice è la percentuale del codice che viene eseguito durante l’esecuzione dei test automatizzati. Uno strumento registra quali righe e, facoltativamente, quali rami sono stati eseguiti, quindi segnala cosa è stato mancato.

È molto utile al contrario. Una bassa copertura in un’area ti dice, con certezza, che i test non la stanno esercitando. Una copertura elevata dice molto meno.

Tipi di copertura

Digitare Domanda a cui risponde
Funzione Ciascuna funzione è stata chiamata almeno una volta?
Riga (istruzione) Ogni linea è stata eseguita almeno una volta?
Ramo Ciascuno if è andato in entrambe le direzioni, vero e falso?
Condizione Ciascuna parte di una condizione combinata (a and b) ha assunto entrambi i valori?
MC/DC Ciascuna condizione ha modificato in modo indipendente il risultato? Utilizzato in software critici per la sicurezza.

Ogni livello è più severo di quello superiore. La maggior parte dei team monitora la copertura di linee e filiali. La copertura delle condizioni e MC/DC compaiono negli standard aeronautici e automobilistici, dove il supporto degli utensili è specializzato.

Il codice che abbiamo misurato

def shipping_fee(total, is_member, express=False):
    if total < 0:
        raise ValueError("total cannot be negative")
    fee = 0 if total >= 1000 else 50
    if is_member:
        fee = max(fee - 25, 0)
    if express:
        fee += 100
    return fee

Il nostro primo file di test aveva due test:

def test_regular_order():
    assert shipping_fee(500, False) == 50

def test_member_express():
    assert shipping_fee(500, True, express=True) == 125

L’abbiamo eseguito con la misurazione del ramo attivata:

coverage run --branch -m pytest test_shipping.py
coverage report -m

Leggendo il rapporto

Name          Stmts   Miss Branch BrPart  Cover   Missing
---------------------------------------------------------
shipping.py       9      1      6      1    87%   3
  • Stmts 9, Miss 1: nove istruzioni eseguibili, una mai eseguita.
  • Ramo 6, BrParte 1: tre if decisioni danno sei possibili risultati. Una decisione è andata sempre e solo in una direzione.
  • Missing 3: riga 3, la raise ValueError, non è mai stata eseguita. Nessun test ha superato un totale negativo.

La correzione è un test per il percorso dell’errore. Abbiamo anche aggiunto un test per un ordine di grandi dimensioni:

def test_free_over_1000():
    assert shipping_fee(1000, False) == 0

def test_negative_total():
    with pytest.raises(ValueError):
        shipping_fee(-1, False)

Con entrambi i file, il rapporto mostrava il 100% e nessuna riga mancante o ramo parziale. I percorsi di errore sono i classici ritrovamenti di copertura dei gap, motivo per cui i test per input non validi, progettati con tecniche come il partizionamento di equivalenza, si accoppiano così bene con esso.

Cosa nascondeva il rapporto

Guarda di nuovo il primo rapporto. Diceva che mancava solo la linea 3. Ma i primi due test ne hanno utilizzati entrambi un totale di 500, quindi la regola di spedizione gratuita per ordini pari o superiori a 1.000 è stata mai testata e il rapporto non l’ha segnalata.

Il motivo è che fee = 0 if total >= 1000 else 50 si trova su una riga. cover.py misura i rami tra le righe, quindi un’espressione condizionale all’interno di una singola riga conta come un’istruzione eseguita. Se quella regola avesse un bug, come > invece di >=, la prima suite avrebbe comunque mostrato l’87% senza alcun suggerimento. I test del confine stesso, come descritto in analisi del valore limite, sono ciò che lo rileva.

Copertura al 100% senza controlli

Successivamente, abbiamo scritto un test deliberatamente scadente che richiama ogni percorso ma non asserisce nulla:

def test_runs_everything_but_checks_nothing():
    shipping_fee(500, True, express=True)
    shipping_fee(1000, False)
    shipping_fee(500, False)
    try:
        shipping_fee(-1, False)
    except ValueError:
        pass
shipping.py       9      0      6      0   100%

Ha raggiunto il 100% di copertura di linee e filiali. La funzione potrebbe restituire qualsiasi numero e il test verrebbe comunque superato. Questo è il limite fondamentale della copertura: misura l’esecuzione, non la verifica.

Suggerimento: quando esamini una richiesta pull, leggi le asserzioni, non solo la modifica della copertura. Un test che aggiunge copertura senza un’asserzione significativa aggiunge solo falsa fiducia.

Per misurare se i test rilevano effettivamente i bug, utilizzare il test di mutazione. Uno strumento come mutmut per Python, PIT per Java o Stryker per JavaScript apporta piccole modifiche al codice, come capovolgere da >= a >, e controlla che un test fallisca. Il nostro test di non asserzione consentirebbe la sopravvivenza di ogni mutazione.

Quale obiettivo fissare

Non esiste un numero universalmente corretto. Guida pratica:

  • Non inseguire il 100%. L’ultima percentuale spesso significa testare codice banale o scrivere test che toccano solo le righe.
  • Molti team puntano a circa il 70-85% in totale, con aspettative più elevate per la logica del core business.
  • Osserva la tendenza, non il numero. La copertura che diminuisce a ogni pubblicazione è un segnale di avvertimento.
  • Richiedere la copertura del nuovo codice nelle richieste pull anziché forzare il vecchio codice tutto in una volta.
  • Escludi ciò che non conta, come il codice generato, con regole chiare.

Si applica la legge di Goodhart: quando una misura diventa un obiettivo, smette di essere una buona misura. Gli obiettivi difficili incoraggiano test come il nostro esempio di non asserzione.

Strumenti per lingua

Lingua Strumenti comuni
Pitone cover.py, pytest-cov
JavaScript/TypeScript Istanbul (New York), integrato in Jest e Vitest
Giava JaCoCo
.NET Coperta
Vai vai al test -cover
Segnalazione Codecov, SonarQube, Tute

Copertura in CI

Esegui la copertura come parte della tua pipeline CI e pubblica il rapporto in modo che i revisori possano vedere quali nuove righe non sono state testate. Con cover.py, coverage report --fail-under=80 fallisce la compilazione al di sotto di una soglia. Mantieni qualsiasi soglia modesta e combinala con una regola sul nuovo codice, in modo che un unico file legacy di grandi dimensioni non blocchi ogni modifica.

Oltre la copertura: domande migliori da porre

  • I test coprono le parti del codice rischiose e frequentemente modificate?
  • Vengono messi alla prova i confini e i percorsi di errore, non solo il percorso felice?
  • Un test fallirebbe se questa riga fosse sbagliata?
  • I test sono sufficientemente veloci e stabili da poter essere eseguiti ad ogni modifica?

Questi appartengono a qualsiasi strategia di test. La copertura aiuta a rispondere solo alla prima.

Esclusione del codice e lettura dei report HTML

Non vale la pena testare direttamente alcuni codici, come gli helper di debug o un blocco che viene eseguito solo quando uno script viene avviato manualmente. cover.py ti consente di escludere le righe con un commento:

if __name__ == "__main__":  # pragma: no cover
    main()

Puoi anche impostare le esclusioni e le cartelle di origine da misurare nella configurazione, ad esempio in pyproject.toml:

[tool.coverage.run]
branch = true
source = ["shop"]

[tool.coverage.report]
show_missing = true
exclude_also = ["if TYPE_CHECKING:"]

Utilizza le esclusioni con parsimonia e rivedile come codice. Ognuna è una decisione secondo cui alcune linee non verranno mai controllate. Escludere un gestore di errori difficile da testare solo per aumentare il numero vanifica lo scopo.

Per il lavoro quotidiano il report HTML è più utile della tabella terminale. Esegui coverage html e apri htmlcov/index.html. Ogni file viene mostrato con le linee eseguite in verde, le linee mancate in rosso e i rami parziali in giallo, così puoi vedere a colpo d’occhio quale if non ha mai preso l’altra strada. Molti editor possono anche mostrare la copertura a margine, rendendo visibili gli spazi vuoti mentre si scrive il codice anziché dopo.

Copertura prima del refactoring

La copertura è anche un controllo di sicurezza prima del refactoring. Se intendi ristrutturare un modulo, guarda prima il suo rapporto. Le aree rosse non hanno test per avvisarti se il comportamento cambia, quindi aggiungi test di caratterizzazione che catturano il comportamento attuale prima di iniziare. Utilizzata in questo modo, la copertura smette di essere un obiettivo e diventa uno strumento pratico di pianificazione.

Lo stesso report aiuta quando un bug sfugge alla produzione. Trova la riga in cui risiedeva il bug e controlla se qualche test lo ha eseguito. In caso contrario, hai trovato una lacuna che vale la pena colmare. Se un test lo ha eseguito, il problema è un’asserzione debole, che è esattamente la situazione mostrata dal nostro esempio senza asserzione. Entrambe le risposte ti dicono come migliorare la suite.

Considerazioni finali

La copertura del codice è una torcia, non un voto. Utilizzare la copertura delle filiali per trovare codice che nessun test tocca, in particolare la gestione degli errori, e quindi scrivere test che facciano asserzioni reali. Se ricordi un risultato della nostra analisi, dillo questo: la copertura del 100% è stata raggiunta da un test che non ha verificato nulla.

Domande frequenti

Qual è la copertura del codice nei test?

È la percentuale di codice eseguito durante l’esecuzione dei test automatizzati, misurata da linee, rami o condizioni. Mostra quali test hanno toccato, non se lo hanno verificato.

Qual è la differenza tra copertura di linea e di filiale?

La copertura della linea controlla che ciascuna linea funzioni. La copertura delle filiali verifica che ogni decisione sia andata in entrambe le direzioni. La copertura delle filiali trova percorsi non testati che mancano alla copertura della linea.

La copertura del codice al 100% è buona?

Non da solo. Nel nostro test, una suite senza asserzioni ha raggiunto il 100%. Punta a test significativi su codici importanti piuttosto che su un numero perfetto.

Qual è una buona percentuale di copertura del codice?

Molti team puntano complessivamente a circa il 70-85%, con una maggiore attenzione alla logica critica. La tendenza e la qualità delle affermazioni contano più del numero esatto.

In che modo il test di mutazione differisce dalla copertura?

Il test di mutazione modifica leggermente il codice e verifica che un test fallisca. Misura se i test rilevano bug, cosa che la copertura non può fare.

Continua a imparare

Lavora con Stack Quora

Hai bisogno di aiuto con test, WordPress o SEO?

Aiutiamo i team a fornire software migliore e a far crescere i loro siti. Raccontaci a cosa stai lavorando e ti suggeriremo chiaramente il prossimo passo.

Continua a leggere

Altre guide su test, intelligenza artificiale e sviluppo moderno

Ti è piaciuto questo articolo? Scegli un argomento e passa direttamente ai nostri tutorial più recenti.

Intelligenza artificiale

Spiegazione delle allucinazioni dell'intelligenza artificiale: perché i modelli linguistici inventano le cose e come ridurle

Spiegazione delle allucinazioni dell'intelligenza artificiale: cosa sono, perché i modelli linguistici le producono, tipi comuni, come ridurle con b…

Leggi l'articolo →
DevOps

Spiegazione delle variabili d'ambiente: file .env, segreti e configurazione eseguiti correttamente

Variabili d'ambiente spiegate con esempi Python che abbiamo eseguito: lettura e impostazione, perché ogni valore è una stringa, file .env, ereditari…

Leggi l'articolo →
Sviluppo software

Spiegazione della limitazione della velocità API: algoritmi, intestazioni e gestione degli errori 429

La limitazione della velocità dell'API spiegata con una simulazione che abbiamo eseguito: bucket di token, finestra fissa, finestra scorrevole, risp…

Leggi l'articolo →
Automazione dei test

Derisione in Python: unittest.mock, patch e gli errori che fanno mentire i test

Mocking in Python con unittest.mock, testato: Mock e return_value, side_effect, patch dove viene utilizzato un nome, specifiche e autospec, date moc…

Leggi l'articolo →
Test del software

Spiegazione della copertura del codice: copertura di linea o di filiale e cosa significa realmente il 100%.

Copertura del codice spiegata con una vera esecuzione di cover.py: copertura di linea, ramo e condizione, lettura di un rapporto, perché la copertur…

Leggi l'articolo →
Database e SQL

DELETE vs TRUNCATE vs DROP in SQL: differenze, rollback e quando utilizzarli

DELETE vs TRUNCATE vs DROP spiegato con SQL testato: cosa rimuove, rollback, trigger, reimpostazione dell'identità, velocità e come differiscono Pos…

Leggi l'articolo →
Database e SQL

Spiegazione delle visualizzazioni SQL: crea, utilizza ed evita le insidie, con esempi

Viste SQL spiegate con le query che abbiamo eseguito: creazione di una vista, risultati in tempo reale, nascondere colonne, prestazioni, viste aggio…

Leggi l'articolo →
Sviluppo software

Interfaccia TypeScript e tipo: differenze reali, testate con il compilatore

Interfaccia TypeScript vs tipo, verificata con il vero compilatore: cosa possono fare entrambi, cosa può fare solo uno, fusione delle dichiarazioni,…

Leggi l'articolo →
Sviluppo software

Spiegazione di Python *args e **kwargs: funzioni flessibili con esempi chiari

Python *args e **kwargs spiegati con il codice che abbiamo eseguito: compressione e decompressione, ordine dei parametri, argomenti solo per parole …

Leggi l'articolo →
Sviluppo software

Funzioni Lambda Python: sintassi, usi reali e quando non usarli

Funzioni lambda Python spiegate con il codice che abbiamo eseguito: sintassi, ordinamento con chiave, mappa e filtro, chiusure, trap dell'associazio…

Leggi l'articolo →
Intelligenza artificiale

Cosa sono gli agenti AI? Come funzionano, con un semplice ciclo di agenti che puoi eseguire

Cosa sono gli agenti IA? Una semplice guida al ciclo dell'agente, agli strumenti, alla memoria e ai guardrail, con un piccolo ciclo Python che abbia…

Leggi l'articolo →
DevOps

Monitoraggio vs osservabilità: la vera differenza, con log, metriche e tracce

Spiegazione del monitoraggio e dell'osservabilità: errori noti e sconosciuti, log, metriche e tracce, una procedura dettagliata sugli incidenti di p…

Leggi l'articolo →
DevOps

Volumi Docker e montaggi di associazione: mantenere i dati del contenitore nel modo giusto

Volumi Docker e montaggi di collegamento spiegati: perché i dati del contenitore scompaiono, volumi denominati, montaggi di collegamento, tmpfs, ese…

Leggi l'articolo →
Automazione dei test

TDD vs BDD: differenze chiave, una caratteristica costruita in entrambi i modi e quando utilizzarli ciascuno

TDD vs BDD spiegato con una funzione di sconto costruita in entrambi i modi: refactoring rosso-verde con il codice che abbiamo eseguito, scenari "Da…

Leggi l'articolo →
Test del software

Test di accettazione da parte dell'utente (UAT): processo, lista di controllo ed esempi reali

Spiegazione del test di accettazione dell'utente: chi lo esegue, in cosa differisce dal QA, un processo in sette fasi, un modello di test case UAT, …

Leggi l'articolo →
Database e SQL

Spiegazione dei valori SQL NULL: IS NULL, COALESCE, NULLIF e le trappole da evitare

Valori SQL NULL spiegati con le query che abbiamo eseguito: logica a tre valori, IS NULL, COALESCE, NULLIF, come aggregazioni, join e ordinamento tr…

Leggi l'articolo →
Database e SQL

Istruzione SQL CASE WHEN: sintassi, esempi reali ed errori comuni

SQL CASE WHEN spiegato con le query che abbiamo eseguito: moduli semplici e cercati, ordine delle condizioni, pivot con SUM, trucchi COUNT, ordiname…

Leggi l'articolo →
Sviluppo software

Cos'è il JWT? Token Web JSON spiegati con un esempio funzionante

Cos'è il JWT? Spiegazione dei token Web JSON: le tre parti, un token creato e manomesso nel codice che abbiamo eseguito, JWT e sessioni, archiviazio…

Leggi l'articolo →
Sviluppo software

Spiegazione del loop di eventi JavaScript: stack di chiamate, microtask e timer con esempi

Il ciclo di eventi JavaScript spiegato con il codice che abbiamo eseguito: stack di chiamate, code di attività e microtask, perché setTimeout 0 non …

Leggi l'articolo →
Sviluppo software

Spiegazione dei generatori Python e del rendimento: risparmia memoria con esempi reali

Generatori Python e rendimento spiegati con il codice che abbiamo eseguito: come vengono messi in pausa e ripresi, misurato il risparmio di memoria,…

Leggi l'articolo →
Intelligenza artificiale

Cosa sono gli incorporamenti? I vettori e la somiglianza del coseno spiegati semplicemente

Cosa sono gli incorporamenti? Una guida in linguaggio semplice ai vettori, alla similarità del coseno e alla ricerca semantica, con un piccolo esemp…

Leggi l'articolo →
DevOps

Distribuzione blu-verde rispetto a quella canaria: differenze, compromessi e come scegliere

Spiegazione della distribuzione blu-verde e canary: come funziona ciascuna, costi, rischi e rollback, bucket utente stabile che abbiamo testato, mig…

Leggi l'articolo →
Automazione dei test

Test instabili: quali sono le cause e come risolverli definitivamente

Test instabili spiegati con demo che abbiamo eseguito: le cause principali, come confermarne e diagnosticarne uno, soluzioni adeguate per tempistich…

Leggi l'articolo →
Test del software

Test di carico e test di stress: differenze, metriche ed esempi k6

Spiegazione del test di carico e dello stress test: cosa dimostra ciascuno, test di picco e di assorbimento, le metriche che contano, perché le medi…

Leggi l'articolo →
Test del software

Caso di test e scenario di test: differenze chiave, esempi e quando utilizzarli

Spiegazione del caso di test e dello scenario di test: definizioni chiare, una tabella affiancata, una funzione di reimpostazione della password pre…

Leggi l'articolo →
Database e SQL

Ottimizzazione delle query SQL: leggi i piani di esecuzione e correggi query lente, con tempistiche

Ottimizzazione delle query SQL testata su un milione di righe: leggi i piani di esecuzione, aggiungi gli indici corretti, correggi filtri non aggior…

Leggi l'articolo →
Database e SQL

SQL UNION vs UNION ALL: differenze, esempi e prestazioni testate

SQL UNION vs UNION ALL spiegati con le query che abbiamo eseguito: duplicati, regole seguite da entrambi, un vero bug di dati, prestazioni misurate,…

Leggi l'articolo →
Sviluppo software

Tutorial sull'API di recupero JavaScript: GET, POST, gestione degli errori e timeout

Un tutorial sull'API JavaScript Fetch testato su un server locale: GET e POST, perché 404 non genera eccezioni, un helper riutilizzabile, stringhe d…

Leggi l'articolo →
Sviluppo software

Gestione dei file Python: lettura, scrittura, CSV, JSON e pathlib con esempi

Gestione dei file Python spiegata con il codice che abbiamo eseguito: modalità aperte, con, lettura di file di grandi dimensioni, CSV, JSON, pathlib…

Leggi l'articolo →
Sviluppo software

Classi e oggetti Python: tutorial OOP con esempi reali

Classi e oggetti Python spiegati con il codice che abbiamo eseguito: __init__, metodi, attributi di classe e istanza, ereditarietà, proprietà, metod…

Leggi l'articolo →
Intelligenza artificiale

Che cos'è un modello linguistico di grandi dimensioni? Come funzionano i LLM, spiegato senza esagerazioni

Cos’è un modello linguistico ampio? LLM spiegati in un linguaggio semplice: token, incorporamenti, attenzione, formazione, finestre di temperatura e…

Leggi l'articolo →
DevOps

Comandi Linux per principianti: la guida essenziale del terminale con esempi

Comandi Linux per principianti, raggruppati per attività: navigazione, file, visualizzazione log, grep, pipe, permessi, processi e archivi, con un e…

Leggi l'articolo →
Automazione dei test

Selenio vs drammaturgo: differenze chiave, codice a confronto e quale scegliere

Selenium vs Playwright a confronto: architettura, attesa, supporto per browser e linguaggio, debug, lo stesso test in entrambi gli strumenti, consig…

Leggi l'articolo →
Test del software

Partizionamento di equivalenza: tecnica di progettazione dei test spiegata con esempi pratici

Spiegazione del partizionamento di equivalenza: come trovare partizioni valide e non valide, esempi funzionanti per età, password e coupon, test che…

Leggi l'articolo →
Test del software

Gravità vs priorità nel test del software: differenze, matrice ed esempi reali

Spiegazione della gravità e della priorità nei test del software: definizioni chiare, una matrice a quattro vie con esempi, scale di livello, chi im…

Leggi l'articolo →
Database e SQL

Esercitazione sulle sottoquery SQL: tipi, sottoquery correlate ed EXISTS con esempi

Un tutorial sulle sottoquery SQL con le query che abbiamo eseguito: sottoquery scalari, IN, FROM e SELECT, sottoquery correlate, EXISTS, trap NOT IN…

Leggi l'articolo →
Database e SQL

Spiegazione di transazioni SQL e ACID: COMMIT, ROLLBACK e livelli di isolamento

Transazioni SQL e ACID spiegate con un bonifico bancario eseguito: COMMIT, ROLLBACK, punti di salvataggio, blocchi, livelli di isolamento, letture s…

Leggi l'articolo →
Sviluppo software

Git merge vs rebase: differenze, esempi e quando usare ciascuno

Git merge vs rebase spiegato con output reale del log, una tabella di confronto, la regola d'oro del rebase e un flusso di lavoro sicuro per i team.

Leggi l'articolo →
Sviluppo software

List comprehension in Python: sintassi, esempi e quando evitarla

List comprehension in Python spiegata: sintassi, filtri, if-else, cicli annidati, tempi misurati e i casi in cui un normale ciclo for è meglio.

Leggi l'articolo →
Database e SQL

SQL JOIN spiegati con esempi (Inner, Left, Right, Full)

Capisci i JOIN SQL passo dopo passo con tabelle e query di esempio: INNER, LEFT, RIGHT, FULL, CROSS e SELF, con errori comuni e consigli sulle prest…

Leggi l'articolo →