Il Code Coverage (copertura del codice) è una metrica che mostra quale percentuale del codice sorgente di un'applicazione viene eseguita durante i test. Aiuta a determinare la qualità dei test, identificare aree non verificate e prioritizzare la scrittura di nuovi test. Secondo Atlassian, 2025, il livello ottimale di copertura è del 70–80% — oltre questa soglia, i costi dei test iniziano a superare i benefici.
Punti chiave
Code Coverage (copertura del codice) è una metrica quantitativa che determina quale parte del codice sorgente dell'applicazione è stata eseguita durante i test. Viene espressa in percentuale e calcolata come rapporto tra linee/rami eseguiti e il totale. Un'elevata copertura non garantisce l'assenza di bug, ma riduce il rischio di errori non rilevati.
La copertura del codice aiuta il team a: trovare aree non testate del codice, prendere decisioni sulle priorità di scrittura dei test e monitorare la dinamica della qualità dei test in CI/CD. Nello sviluppo mobile, la copertura è particolarmente importante per la logica di business, i modelli di dati e i repository — i livelli in cui la probabilità di errori è più alta.
Un mito comune: “100% di copertura = qualità perfetta”. In pratica, la copertura al 100% è estremamente rara e spesso ottenuta a costo di test superficiali. La copertura efficace non è una corsa alla percentuale, ma una copertura strategica dei percorsi critici e dei casi limite. La copertura non dice nulla sulla qualità dei test stessi: un test può superare ma non verificare la correttezza del risultato.
Esistono diverse metriche di Code Coverage, ognuna misura aspetti diversi dei test. Line coverage (copertura di linea) è la metrica più semplice, che mostra la percentuale di linee di codice eseguite. Branch coverage (copertura di ramo) misura quali ramificazioni if-else e switch sono state testate.
Line coverage conta ogni linea di codice sorgente come eseguita o meno. Se una linea contiene un operatore condizionale o un ciclo, la linea è considerata eseguita se il controllo l'ha raggiunta, anche se non tutti i rami sono stati elaborati. Questa è la metrica meno rigorosa, ma la più comprensibile per la valutazione visiva.
Branch coverage valuta se tutti i possibili rami nel codice sono stati testati. Per ogni if-else, entrambi i rami sono considerati: true e false. Per switch, ogni case è considerato. Branch coverage è considerata una metrica più rigorosa di line coverage e rivela più spesso scenari non testati.
| Metrica | Cosa misura | Difficoltà di raggiungimento |
|---|---|---|
| Line | Percentuale di linee di codice eseguite | Bassa |
| Branch | Percentuale di rami eseguiti (if/else, switch) | Media |
| Function | Percentuale di funzioni e metodi chiamati | Bassa |
| Condition | Percentuale di sottoespressioni logiche (&&, ||) | Alta |
Path coverage è la metrica più rigorosa, che richiede la verifica di tutte le possibili combinazioni di rami in una funzione. In pratica, path coverage è raramente utilizzato a causa della crescita esponenziale del numero di combinazioni: una funzione con 10 rami ha 1024 percorsi possibili.
Nello sviluppo mobile, vengono utilizzati vari strumenti per misurare il Code Coverage a seconda della piattaforma. Per Android, lo standard è JaCoCo (Java Code Coverage), che si integra con Gradle e supporta sia test unitari che test di strumentazione. Per iOS viene utilizzato XCCov, integrato in Xcode.
JaCoCo genera report nei formati HTML, XML e CSV. Il report HTML evidenzia visivamente le linee: verde — eseguite, rosso — saltate, giallo — parzialmente eseguite. Il report XML è compatibile con SonarQube e altri sistemi di analisi del codice. JaCoCo supporta il filtraggio delle classi: è possibile escludere codice generato, databinding e BuildConfig.
// build.gradle — configurazione JaCoCo
android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
// Generazione report JaCoCo
task jacocoTestReport(type: JacocoReport) {
dependsOn 'testDebugUnitTest'
reports {
xml.enabled = true
html.enabled = true
}
}
XCCov è uno strumento integrato in Xcode per misurare la copertura del codice. Si attiva tramite Gather coverage data nello schema di test. XCCov supporta la copertura per Swift e Objective-C, genera report in formato .xccovreport e si integra con CI tramite xcodebuild -enableCodeCoverage YES. I dati vengono visualizzati nella console e possono essere esportati in JSON.
Per il monitoraggio centralizzato della copertura, vengono utilizzate piattaforme come SonarQube (analisi della qualità del codice + copertura), Codecov e Coveralls. Questi servizi aggregano dati da JaCoCo e XCCov, mostrano tendenze, Quality Gate e integrazione con GitHub/GitLab tramite commenti PR.
Migliorare il Code Coverage richiede un approccio sistematico: non “aumentare le percentuali”, ma coprire i rischi. Il primo passo è analizzare il report JaCoCo o XCCov — identificare le classi rosse (non coperte). Priorità: logica di business → repository → ViewModel → componenti UI.
Il Test-Driven Development (TDD) garantisce automaticamente un'elevata copertura, poiché i test vengono scritti prima dell'implementazione. Il processo: rosso (scrivere un test che fallisce) → verde (scrivere il codice minimo) → refactoring. TDD disciplina lo sviluppatore, costringendolo a coprire casi limite e situazioni eccezionali che spesso rimangono senza test.
Un test parametrizzato sostituisce decine di test ordinari. JUnit e XCTest supportano la parametrizzazione: @ParameterizedTest in JUnit 5, XCTestCase con testPerformanceExample in XCTest. La parametrizzazione consente di verificare molteplici dati di input senza duplicazione del codice, espandendo significativamente la copertura di rami e condizioni.
// Test parametrizzato in Kotlin con JUnit 5
@ParameterizedTest
@ValueSource(strings = ["user@test.com", "admin@test.com", "test@example.com"])
fun `validate email returns true for valid addresses`(email: String) {
val result = EmailValidator.isValid(email)
Assertions.assertTrue(result)
}
L'errore più comune è inseguire la percentuale senza analizzare la qualità dei test. Il team inizia a scrivere test per i test: verificare getter e setter, duplicare la copertura a diversi livelli, testare metodi banali. Questo dà una percentuale elevata ma non migliora la qualità reale.
Un Code Coverage elevato può creare la falsa sensazione che l'applicazione sia ben testata. Un test può eseguire una linea di codice ma non verificare la correttezza del risultato. Ad esempio: un test chiama un metodo di calcolo dello sconto ma non verifica l'importo — la linea viene eseguita, la copertura aumenta, ma il bug non viene trovato.
Un errore tipico è testare solo il “percorso felice” (happy path) e ignorare i casi limite: liste vuote, valori null, numeri massimi, formati errati. È proprio ai limiti e alle eccezioni che si verificano la maggior parte dei bug. Branch coverage aiuta a identificare i rami mancati, ma non garantisce la verifica dei valori limite.
Il Mutation Testing è un metodo per valutare la qualità dei test in cui vengono introdotte mutazioni (errori artificiali) nel codice sorgente e si verifica se i test falliscono. Pitest è un popolare strumento di mutation testing per Java e Kotlin. Se i test non falliscono su una mutazione, significa che non verificano quella condizione.
Pitest crea mutanti — copie modificate del codice sorgente in cui, ad esempio, > viene sostituito con >=, true con false, o una chiamata a metodo viene rimossa. Quindi, per ogni mutante, vengono eseguiti i test. Se i test superano — il mutante è sopravvissuto, il che significa che i test non coprono quello scenario. Se i test falliscono — il mutante è stato ucciso, il test è valido.
// build.gradle — configurazione Pitest
plugins {
id 'info.solidsoft.pitest' version '1.15.0'
}
pitest {
targetClasses = ['com.example.app.*']
targetTests = ['com.example.app.*Test']
threads = 4
outputFormats = ['HTML', 'XML']
mutationThreshold = 80
coverageThreshold = 85
}
Pitest supporta molti tipi di mutazione: modifica degli operatori condizionali (== → !=, < → <=), rimozione di chiamate a metodi, sostituzione dei valori di ritorno (true → false), modifica delle operazioni aritmetiche (+ → -), mutazione degli incrementi (i++ → i--). Più tipi di mutazione vengono uccisi dai test, più la suite di test è affidabile.
Il mutation score target è dell'80% o superiore. Ciò significa che l'80% degli errori artificiali viene rilevato dai test. Una copertura del codice (Code Coverage) del 90% non garantisce che i test trovino bug — il mutation testing fornisce una valutazione più obiettiva. Pitest può essere integrato nel CI come Quality Gate, bloccando il build se il mutation score scende sotto la soglia.
Per il controllo automatico del Code Coverage in CI/CD, vengono utilizzati Quality Gate — valori soglia che, quando violati, contrassegnano il build come instabile o lo respingono. SonarQube consente di configurare un Quality Gate basato su una combinazione di metriche: copertura (≥80%), numero di bug, vulnerabilità e codice duplicato.
In GitHub Actions, il Code Coverage viene integrato attraverso passaggi di azione: eseguire test con copertura → caricare il report su Codecov → verificare la soglia. Codecov commenta automaticamente le PR con il diff di copertura, mostrando quali linee sono cambiate e come ciò ha influenzato la percentuale complessiva. Se la copertura è diminuita, la PR viene bloccata fino a quando non vengono scritti test aggiuntivi.
# GitHub Actions — caricamento copertura su Codecov
- name: Run Tests with Coverage
run: ./gradlew testDebugUnitTest jacocoTestReport
- name: Upload to Codecov
uses: codecov/codecov-action@v4
with:
files: ./app/build/reports/jacoco/jacocoTestReport.xml
flags: unittests
fail_ci_if_error: true
- name: Check Coverage Threshold
run: |
coverage=$(grep -oP 'branchCoverage="\K[0-9.]+' report.xml)
if (( $(echo "$coverage < 80" | bc -l) )); then
echo "Coverage $coverage% is below 80% threshold"
exit 1
fi
I report HTML di JaCoCo e XCCov contengono evidenziazione visiva della copertura: verde — linee eseguite, rosso — non eseguite. SonarQube mostra inoltre la copertura a livello di file, classe, metodo e linea, nonché la cronologia delle modifiche della copertura per sprint. Ciò aiuta a prendere decisioni sul refactoring e l'aggiunta di test.
Domande frequenti
Per i progetti mobile, una copertura del 70–80% per la logica di business e del 50–60% per i componenti UI è considerata buona. Oltre l'80%, i costi dei test iniziano a superare i benefici. È importante ricordare che la percentuale non è un obiettivo ma un indicatore, e diversi moduli possono avere diversi livelli target.
Line Coverage mostra quante linee di codice sono state eseguite. Branch Coverage mostra quante ramificazioni (if-else, switch) sono state testate. Una linea con if può essere eseguita, ma potrebbe essere stato testato solo il ramo true e non il false. Branch Coverage è più rigorosa e rivela più scenari mancati.
In CI/CD, la copertura viene integrata tramite un Quality Gate: il build viene bloccato se la copertura è sotto la soglia. Per Android si usa JaCoCo + SonarQube; per iOS — xcodebuild -enableCodeCoverage con parsing di .xccovreport. GitHub Actions ha azioni pronte per Codecov.
Sì, JaCoCo supporta Jetpack Compose attraverso il meccanismo standard di copertura JVM. Tuttavia, il codice Compose contiene molte espressioni lambda generate che JaCoCo potrebbe non coprire completamente. Si consiglia di escludere il codice Compose generato dal report tramite filtri.
La falsa copertura si verifica quando un test esegue codice ma non verifica il risultato. Soluzione: scrivere asserzioni per ogni scenario importante, utilizzare il mutation testing (Pitest) per verificare la qualità dei test, analizzare non solo la percentuale ma anche quali rami sono coperti.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche