Nasza strona używa cookies. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Co daje code coverage?

Dan Goslen Senior Software Engineer / Bandwidth Inc.
Sprawdź, czym jest code coverage i czy warto w ogóle dążyć do jak najwyższego pokrycia kodu.
Co daje code coverage?

Code coverage (znane też jako test pokrycia) to kolejny temat, który dzieli programistów. Niektórzy upierają się, że stuprocentowe pokrycie jest standardem, a inni, zwłaszcza wiele osobistości z branży IT, twierdzą, że standard znajduje się gdzieś między 80%, a 90%.

Czym zatem jest code coverage? I co w ogóle wnosi do Twojego zespołu?


Code Coverage to kolejna metryka

Code Coverage jest jak każda inna metryka kontroli jakości, której używa się do monitorowania jakości bazy kodu. Jest też prawdopodobnie najprostsza. Chociaż nie będę sprawdzać każdego aspektu testów pokrycia (linia kodu vs. gałąź wykonania vs. warunki itp.), to raporty informują o odsetku kodu wykonanego podczas testów.

Pokrycie gromadzi się przez wywołanie testów za pomocą specjalnego agenta lub flagi kompilacji, która śledzi bloki kodu wykonywane podczas uruchamiania testów. Każde narzędzie do testów pokrycia działa trochę inaczej, ale większość z nich tworzy „agenta”, który wykonuje instrumentację w Twoim źródle (Jacoco i Coverage.py działają w taki właśnie sposób) lub ma dostęp do wewnętrznie generowanych plików używanych do debugowania, aby śledzić, które linie zostają wykonane.

Niezależnie od tego, jak działają one wewnętrznie, większość narzędzi związanych z code coverage generuje raport w postaci pliku .html i podaje listę pokrycia dla każdego pakietu, klasy itp. Raport ten zawiera ogólny procent pokrycia bazy kodu. Liczba ta reprezentuje wtedy to, co byście odpowiedzieli na pytanie od swojego kierownika lub współpracownika o to, jaki jest poziom pokrycia kodu w danym projekcie. 


Code Coverage to walidator

Narzędzia do sprawdzenia code coverage sprawdzają, czy dobrze rozumiesz testy. Kiedy je piszemy (zwłaszcza jednostkowe), często chcemy sprawdzić określoną część naszego kodu. Code Coverage może nam pomóc w potwierdzeniu, czy rzeczywiście testujemy prawidłowy kod. To właśnie sprawia, że jest to mechanizm kontroli jakości Twojej bazy kodu.

Jeśli masz 100 testów, ale wszystkie w kółko uruchamiają ten sam kod, to większość z tych testów nie jest potrzebna. Możesz się chwalić, że masz dużo testów, ale tylko jeden robi coś pożytecznego. Teoretycznie zatem sensowne byłoby, aby zespoły dążyły do wysokiego code coverage. Im więcej pokrytych linii, tym więcej przetestowanego kodu, prawda?

No niestety nie do końca. A oto kilka powodów dlaczego.


Pokryte linie != pokryte zachowanie

Jest to subtelna, ale kluczowa różnica w rozumieniu code coverage. To, że uruchomiłeś wiersz, nie oznacza, że poprawnie potwierdziłeś zachowanie z nim związane. W skrajnych przypadkach może to być test, który w ogóle nie zawiera żadnych asercji. Wywołuje on po prostu metody i uruchamia pojedyncze linie. Brzmi to absurdalnie, ale nawet nie wiesz, jak łatwo popełnić taki błąd samemu.  

Wystarczy tak subtelny szczegół, jak zmiana wymagań. 

Weźmy na przykład prostą funkcję, która zwraca listę elementów. Powiedzmy, że wymaganiem jest posortowanie listy malejąco. Jeśli wcześniej przeprowadziłeś test, który potwierdził, że dana lista ma oczekiwane pozycje, to pewnie nie zadbałeś o to, żeby były w porządku malejącym. 

Jeśli wykonałeś sortowanie, to uruchom test, a po jego zakończeniu wyjdzie, że code coverage wynosi 100% — nie zauważyłeś jednak, że lista jest posortowana rosnąco :) Wiem, że to trywialny przykład, ale pokazuje, że code coverage nie powinno być głównym przedmiotem Twoich testów - powinieneś raczej skupić się na testowaniu zachowań!

Jeśli skoncentrujesz się na pisaniu dobrych testów, które sprawdzą wszystkie aspekty zamierzonego zachowania, uzyskasz wysoki wskaźnik code coverage.


Code Coverage jako Twój przewodnik

Mając to na uwadze, narzędzia związane z code coverage zmieniają się w drogowskazy, które pokazują, gdzie może być konieczne skupienie się na testowaniu, a nie na osiągnięciu określonej liczby procent. Możesz uruchomić testy, przejrzeć raporty o code coverage i podjąć decyzję, czy brakujące pokrycie jest czymś, czemu należałoby się przyjrzeć. 

Bardzo podoba mi się post Martina Fowlera dotyczący takiego toku rozumowania, a najbardziej lubię poniższy fragment:

Jeśli pewien poziom pokrycia zostanie ustalony jako cel, to ludzie będą próbowali go zrealizować. Problem polega na tym, że wysokie wartości code coverage są zbyt łatwe do osiągnięcia przy niskiej jakości testów.


Określenie celu może sprawić, że testy będą słabe, bo będziemy się starali jak najszybciej ten cel osiągnąć, zamiast pisać kod i testy wysokiej jakości. 


Czy 100% jest zatem tego warte?

Mając to wszystko na uwadze, to czy Twoim celem powinno być code coverage na poziomie 100%? Czy jest to w ogóle osiągalne? Istnieje dość krótka praca naukowa na ten temat zatytułowana „Is 100% Test Coverage a Reasonable Requirement?”.

Naukowcy przeprowadzili badanie w ramach dwuletniego projektu, mierząc wskaźniki pokrycia kodu, a także wywiady z zespołem programistów.

  1. Wynik 100% jest jak najbardziej do osiągnięcia.
  2. Być może warto go osiągać.
  3. Code coverage nie jest najlepszym wskaźnikiem jakości, a wysokie pokrycie nie powinno być celem.


Mając to na uwadze, sugerowałbym, że 100% prawdopodobnie nie jest warte zachodu. W przypadku większości projektów jest to po prostu zbyt kosztowne i wiąże się z ironicznym ryzykiem promowania leniwego podejścia do testowania. Jeśli nadal chcesz wyegzekwować pewne wymagania dotyczące code coverage, to zespoły, z którymi pracowałem, wymusiły je na poziomie 80% całego nowego kodu.

To wymaganie pomaga potwierdzić, że przeprowadzone testy są wystarczające. Co więcej, nie wymaga to aktualizacji całego projektu z dnia na dzień i prawdopodobnie nie jest też zbyt kosztowne, nie wspominając już o tym, że pomaga promować małe i częste zmiany.

Skup się przede wszystkim na pisaniu kodu, który jest testowalny! Pamiętaj, że code coverage zwiększa się przez zmniejszanie całkowitej liczby wierszy z tymi samymi testami; czy możesz uprościć tego ifa z dwoma boolami do jednego enuma o nazwie "state"?

Mentalność ta jest warta więcej niż osiągnięcie danej liczby.

Przyjemnego kodowania!

Chciałbym tutaj jednak zaznaczyć, że mój zespół egzekwuje sprawdzenie gałęzi wykonania na poziomie 100% w naszych aplikacjach. Zdajemy sobie jednak sprawę, że niektóre klasy wymagają pisania bezużytecznych testów, które nie weryfikują, czy kod będzie działał w środowisku produkcyjnym.

Typowym przykładem jest poprawne zapytanie SQL. Testy te powinny być wykonywane jako testy integracyjne lub testy typu end-to-end na rzeczywistej bazie danych, z poprawnym schematem i wersjami silnika bazy danych.

Mając to na uwadze, nie mamy nic przeciwko dodawaniu takich klas do naszych list wykluczeń ze sprawdzania pokrycia, ale dodanie każdej klasy wymaga zgody zespołu.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

Nie przegap treści Bulldogjob
Subskrybuj artykuły

Lubisz dzielić się wiedzą i chcesz zostać autorem?

Podziel się wiedzą z 160 tysiącami naszych czytelników

Dowiedz się więcej