Code Coverage (pokrycie kodu) to metryka pokazująca, jaki procent kodu źródłowego aplikacji jest wykonywany podczas testowania. Pomaga określić jakość testów, zidentyfikować niesprawdzone obszary i ustalić priorytety pisania nowych testów. Według Atlassian, 2025, optymalny poziom pokrycia wynosi 70–80% — powyżej tego progu koszty testów zaczynają przewyższać korzyści.
Najważniejsze
Code Coverage (pokrycie kodu) to ilościowa metryka, która określa, jaka część kodu źródłowego aplikacji została wykonana podczas testów. Wyrażana jest w procentach i obliczana jako stosunek liczby wykonanych linii/gałęzi do całkowitej liczby. Wysokie pokrycie nie gwarantuje braku błędów, ale zmniejsza ryzyko przeoczonych usterek.
Pokrycie kodu pomaga zespołowi: znajdować nieprzetestowane obszary kodu, podejmować decyzje o priorytetach pisania testów, śledzić dynamikę jakości testowania w CI/CD. W rozwoju aplikacji mobilnych pokrycie jest szczególnie ważne dla logiki biznesowej, modeli danych i repozytoriów — warstw, w których prawdopodobieństwo błędów jest najwyższe.
Popularny mit: „100% pokrycia = idealna jakość“. W praktyce 100% pokrycia osiąga się niezwykle rzadko i często kosztem powierzchownych testów. Efektywne pokrycie to nie wyścig o procent, ale strategiczne pokrycie krytycznych ścieżek i warunków brzegowych. Pokrycie nie mówi nic o jakości samych testów: test może przechodzić, ale nie sprawdzać poprawności wyniku.
Istnieje kilka metryk Code Coverage, z których każda mierzy różne aspekty testowania. Line coverage (pokrycie linii) to najprostsza metryka pokazująca procent wykonanych linii kodu. Branch coverage (pokrycie gałęzi) mierzy, które rozgałęzienia if-else i switch zostały przetestowane.
Line coverage liczy każdą linię kodu źródłowego jako wykonaną lub nie. Jeśli w linii znajduje się operator warunkowy lub pętla, linia jest uznawana za wykonaną, jeśli sterowanie do niej dotarło, nawet jeśli nie wszystkie gałęzie zostały obsłużone. To najmniej rygorystyczna metryka, ale najbardziej zrozumiała do oceny wizualnej.
Branch coverage ocenia, czy wszystkie możliwe rozgałęzienia w kodzie zostały przetestowane. Dla każdego if-else uwzględniane są obie gałęzie: true i false. Dla switch — każdy case. Branch coverage jest uznawana za bardziej rygorystyczną metrykę niż line coverage i częściej wykrywa niesprawdzone scenariusze.
| Metryka | Co mierzy | Trudność osiągnięcia |
|---|---|---|
| Line | Procent wykonanych linii kodu | Niska |
| Branch | Procent wykonanych rozgałęzień (if/else, switch) | Średnia |
| Function | Procent wywołanych funkcji i metod | Niska |
| Condition | Procent logicznych podwyrażeń (&&, ||) | Wysoka |
Path coverage to najbardziej rygorystyczna metryka, wymagająca sprawdzenia wszystkich możliwych kombinacji rozgałęzień w funkcji. W praktyce path coverage jest rzadko używane ze względu na wykładniczy wzrost liczby kombinacji: funkcja z 10 rozgałęzieniami ma 1024 możliwych ścieżek.
W rozwoju aplikacji mobilnych stosuje się różne narzędzia do pomiaru Code Coverage w zależności od platformy. Dla Android standardem jest JaCoCo (Java Code Coverage), który integruje się z Gradle i obsługuje zarówno testy jednostkowe, jak i testy instrumentalne. Dla iOS używany jest XCCov, wbudowany w Xcode.
JaCoCo generuje raporty w formatach HTML, XML i CSV. Raport HTML wizualnie podświetla linie: zielone — wykonane, czerwone — pominięte, żółte — częściowo wykonane. Raport XML jest kompatybilny z SonarQube i innymi systemami analizy kodu. JaCoCo obsługuje filtrowanie klas: można wykluczyć generated-kod, databinding, BuildConfig.
// build.gradle — konfiguracja JaCoCo
android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
// Tworzenie raportu JaCoCo
task jacocoTestReport(type: JacocoReport) {
dependsOn 'testDebugUnitTest'
reports {
xml.enabled = true
html.enabled = true
}
}
XCCov — wbudowane narzędzie Xcode do pomiaru pokrycia kodu. Włącza się przez Gather coverage data w schemacie testowania. XCCov obsługuje pokrycie dla Swift i Objective-C, generuje raporty w .xccovreport i integruje się z CI przez xcodebuild -enableCodeCoverage YES. Dane są wyświetlane w konsoli i mogą być eksportowane do JSON.
Do scentralizowanego monitorowania pokrycia wykorzystuje się platformy: SonarQube (analiza jakości kodu + pokrycie), Codecov i Coveralls. Te serwisy agregują dane z JaCoCo i XCCov, pokazują trendy, Quality Gate i integrację z GitHub/GitLab przez komentarze PR.
Zwiększanie Code Coverage wymaga systematycznego podejścia: nie „dobić procentów”, ale zamykać ryzyka. Pierwszym krokiem jest analiza raportu JaCoCo lub XCCov — identyfikacja czerwonych (niepokrytych) klas. Priorytet: logika biznesowa → repozytoria → ViewModel → komponenty UI.
Test-Driven Development (TDD) automatycznie zapewnia wysokie pokrycie, ponieważ testy są pisane przed implementacją. Proces: czerwony (napisz test, który nie przechodzi) → zielony (napisz minimalny kod) → refaktoryzacja. TDD dyscyplinuje programistę, zmuszając do pokrycia przypadków brzegowych i wyjątków, które często pozostają bez testów.
Jeden test parametryzowany zastępuje dziesiątki zwykłych. JUnit i XCTest obsługują parametryzację: @ParameterizedTest w JUnit 5, XCTestCase z testPerformanceExample w XCTest. Parametryzacja pozwala sprawdzić wiele danych wejściowych bez duplikowania kodu, co znacznie rozszerza pokrycie gałęzi i warunków.
// Test parametryzowany w Kotlin z 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)
}
Najczęstszy błąd to wyścig o procent bez analizy jakości testów. Zespół zaczyna pisać testy dla testów: sprawdza gettery i settery, dubluje pokrycie na różnych poziomach, testuje trywialne metody. Daje to wysoki procent, ale nie podnosi rzeczywistej jakości.
Wysoki Code Coverage może stworzyć złudne wrażenie, że aplikacja jest dobrze przetestowana. Test może wykonać linię kodu, ale nie sprawdzić poprawności wyniku. Na przykład: test wywołuje metodę obliczania rabatu, ale nie sprawdza kwoty — linia wykonana, pokrycie rośnie, a błąd nie został znaleziony.
Typowy błąd — testowanie tylko „szczęśliwej ścieżki“ (happy path) i ignorowanie przypadków brzegowych: pustych list, wartości null, maksymalnych liczb, nieprawidłowych formatów. To właśnie na granicach i wyjątkach występuje większość błędów. Branch coverage pomaga wykryć pominięte gałęzie, ale nie gwarantuje sprawdzenia wartości brzegowych.
Mutation Testing to metoda oceny jakości testów, w której do kodu źródłowego wprowadza się mutacje (sztuczne błędy) i sprawdza, czy testy zawiodą. Pitest — popularne narzędzie mutation testing dla Java i Kotlin. Jeśli testy nie zawiodły na mutacji — oznacza to, że nie sprawdzają danego warunku.
Pitest tworzy mutanty — zmodyfikowane kopie kodu źródłowego, w których na przykład > zastąpiono >=, true na false lub usunięto wywołanie metody. Następnie dla każdego mutanta uruchamiane są testy. Jeśli testy przechodzą — mutant przeżył, oznacza to, że testy nie pokrywają tego scenariusza. Jeśli testy zawodzą — mutant został zabity, test jest poprawny.
// build.gradle — konfiguracja 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 obsługuje wiele rodzajów mutacji: zmiana operatorów warunkowych (== → !=, < → <=), usunięcie wywołań metod, zamiana zwracanych wartości (true → false), zmiana operacji arytmetycznych (+ → -), mutacja inkrementacji (i++ → i--). Im więcej rodzajów mutacji zostało zabitych przez testy, tym bardziej niezawodny zestaw testów.
Docelowy mutation score — 80% i więcej. Oznacza to, że 80% sztucznych błędów zostało wykrytych przez testy. Pokrycie kodu (Code Coverage) na poziomie 90% nie gwarantuje, że testy znajdują błędy — mutation testing daje bardziej obiektywną ocenę. Pitest może być zintegrowany z CI jako Quality Gate, blokując kompilację przy spadku mutation score poniżej progu.
Do automatycznej kontroli Code Coverage w CI/CD stosuje się Quality Gates — wartości progowe, przy przekroczeniu których kompilacja jest oznaczana jako niestabilna lub odrzucana. SonarQube pozwala skonfigurować Quality Gate na podstawie kombinacji metryk: pokrycie (≥80%), liczba błędów, podatności i zduplikowanego kodu.
W GitHub Actions Code Coverage integruje się przez kroki akcji: uruchomienie testów z pokryciem → przesłanie raportu do Codecov → sprawdzenie progu. Codecov automatycznie komentuje PR z diffem pokrycia, pokazując, które linie się zmieniły i jak to wpłynęło na ogólny procent. Jeśli pokrycie spadło — PR jest blokowany do czasu napisania dodatkowych testów.
# GitHub Actions — przesyłanie pokrycia do 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
Raporty HTML JaCoCo i XCCov zawierają wizualne podświetlenie pokrycia: zielony — wykonane linie, czerwony — niewykonane. SonarQube dodatkowo pokazuje pokrycie na poziomie pliku, klasy, metody i linii, a także historię zmian pokrycia w sprintach. Pomaga to podejmować decyzje o refaktoryzacji i dodawaniu testów.
Często zadawane pytania
Dla projektów mobilnych za dobre uważa się pokrycie 70–80% dla logiki biznesowej i 50–60% dla komponentów UI. Powyżej 80% koszty testowania zaczynają przewyższać korzyści. Ważne jest, aby pamiętać, że procent nie jest celem, ale wskaźnikiem, a różne moduły mogą mieć różne docelowe poziomy.
Line Coverage pokazuje, ile linii kodu zostało wykonanych. Branch Coverage — ile rozgałęzień (if-else, switch) zostało przetestowanych. Linia z if może być wykonana, ale gałąź true przetestowana, a false — nie. Branch Coverage jest bardziej rygorystyczny i wykrywa więcej pominiętych scenariuszy.
W CI/CD pokrycie integruje się przez Quality Gate: kompilacja jest blokowana, jeśli pokrycie jest poniżej progu. Dla Android używa się JaCoCo + SonarQube, dla iOS — xcodebuild -enableCodeCoverage z parsowaniem .xccovreport. GitHub Actions ma gotowe akcje dla Codecov.
Tak, JaCoCo obsługuje Jetpack Compose przez standardowy mechanizm pokrycia JVM. Jednak kod Compose zawiera wiele wygenerowanych wyrażeń lambda, które JaCoCo może nie w pełni pokrywać. Zaleca się wykluczanie wygenerowanego kodu compose z raportu przez filtry.
Fałszywe pokrycie powstaje, gdy test wykonuje kod, ale nie sprawdza wyniku. Rozwiązanie: pisać assercje dla każdego ważnego scenariusza, używać mutation testing (Pitest) do sprawdzenia jakości testów, analizować nie tylko procent, ale także które gałęzie są pokryte.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również