Wątek przeniesiony 2022-08-21 20:01 z Edukacja przez cerrato.

Ile powinno wynosic Code Coverage?

0

Pytanie z rozmowy rekrutacyjnej:

"Ile powinno wynosic Code Coverage?"

80% czy jest do zależne od tego co się pisze?

0

Nie ma reguły, ale jeżeli ktoś pyta na rekrutacji to musisz się wstrzelić w odpowiedź :)

Albo to będzie zależy, albo jakiś % typu 80/90/100 itd.

0

Nada przetestować kluczową logikę kodu, może nie wszystkie gettery i settery muszą być pokryte; ale jak metoda ma, if, to dwa testy by się przydały:)

2

Coverage powinien dążyć do 100%, zostawianie kodu bez testów powinno być wyjątkiem, a nie regułą i powinno być dobrze uzasadnione. W szczególności powinno być dobrze weryfikowane czy faktycznie czegoś nie da się przetestować czy komuś się nie chce, bo trzeba się narobić przy tym.

lion137 napisał(a):

Nada przetestować kluczową logikę kodu, może nie wszystkie gettery i settery muszą być pokryte; ale jak metoda ma, if, to dwa testy by się przydały:)

Jeśli getter i setter nie są pokryte to znaczy, że są nieużywane w logice, czyli można je usunąć. To powinno pokryć się samo przy testowaniu większych fragmentów kodu.

0

@Saalin:

Coverage powinien dążyć do 100%

Tak

zostawianie kodu bez testów powinno być wyjątkiem, a nie regułą i powinno być dobrze uzasadnione.

No nie wiem, jest dużo kodu który jest nudnym wręcz przypominającym boilerplate kodem

I czasem szkoda czasu na testowanie tego

1
1a2b3c4d5e napisał(a):

No nie wiem, jest dużo kodu który jest nudnym wręcz przypominającym boilerplate kodem

I czasem szkoda czasu na testowanie tego

Trudno mi się odnieść bez przykładu, ale w kodzie boilerplate też można się pomylić.

5

najlepiej 100% use casów. Jakiekolwiek wyznaczanie procent pokrycia dla kodu (klas, linijek itd.) nie ma żadnego sensu.

2

U mnie w projekcie senior wymaga 100% pokrycia. Inaczej MR nie przechodzi :P wprawdzie projekt jeszcze nie wszedł na produkcję i dopiero za chwilę będą gonić terminy, dlatego jeszcze trzymamy się powyższej zasady.

0

@Saalin:

jakiś kod do logów, kod do debugowania

ok, w kodzie prostym też można się pomylić, ale jeżeli widzisz że to będzie coś, co piszę się raz i nigdy więcej nie tyka (np. jakiś dump danych do JSONa/CSV/blabla), to może wystarczy to raz odpalić i tyle?

4

Coverage nie jest żadnym wyznacznikiem przetestowania kodu. Łatwo można osiągnąć 100% coverage, w taki sposób że testy nic nie testują. Dla przykładu, w idealnie przetestowanym kodzie wystarczy usunąć asercje i verify z testów. Nadal mamy idealne 100%, ale testy są useless.

Dla mnie jedyną akceptowalną coverage jest 100%, ale to tylko pierwszy krok. Dalszym krokiem byłby np Mutation testing score.

still.still napisał(a):

80% czy jest do zależne od tego co się pisze?

Ta liczba 80% w mojej opinii się wzięło stąd, że jak napiszesz taką standardową aplikację lub bibliotekę, to jak otestujesz samą logikę taką "przy krawędzi" (typu settery, repository, etc.) to dostaniesz właśnie coś około 4/5, a pozostałe 1/5 to część logiki która jest trudniejsza do przetestowania, więc zostawia się ją już nie przetestowaną.

Testowanie aplikacji poprawnie jest po prostu bardzo trudne, mało kto umie to zrobić dobrze. Znacznie łatwiej jest sobie wytworzyć wizję dobrze przetestowanego kodu takimi testami-słupami i heja, testować settery i gettery, ustawić próg coverage na 80% i już mamy wiecznie zielone testy, nie trzeba się nimi martwić :> A raz na jakiś czas bug na produkcji się może zdarzyć.

1
Riddle napisał(a):

Coverage nie jest żadnym wyznacznikiem przetestowania kodu.

Coverage jest całkiem dobrym wskaźnikiem, bo po niskim coverage od razu widać, że jest słabo.

4

@Saalin:

Testy automatyczne (unit testy, e2e, blabla) to nie jedyny sposób testowania softu.

Z samej uwagi na to ta metryka jest trochę średnia, trzeba rozważać przypadki indywidualne.

4
Saalin napisał(a):
Riddle napisał(a):

Coverage nie jest żadnym wyznacznikiem przetestowania kodu.

Coverage jest całkiem dobrym wskaźnikiem, bo po niskim coverage od razu widać, że jest słabo.

Tyle, że przy wysokim nie dowodzi, że jest dobrze

1

Jeżeli dany kawałek kodu nie jest w ogóle testowany to powstaje pytanie dlaczego ktoś tego kawałka jeszcze nie wyrzucił?

Drugie pytanie to czy code coverage jest liczony dobrze. Czy testu manualne też się wliczają w tę metrykę?

2
Saalin napisał(a):

Jeśli getter i setter nie są pokryte to znaczy, że są nieużywane w logice, czyli można je usunąć. To powinno pokryć się samo przy testowaniu większych fragmentów kodu.

No nie wiem, mogą być używane przez jakieś bazujące na refleksji narzędzia typu mapper albo ORM, i już nie będą liczone do statystyk.

Saalin napisał(a):

Coverage jest całkiem dobrym wskaźnikiem, bo po niskim coverage od razu widać, że jest słabo.

Zgadzam się, że jest słabo, ale nie nazwał bym wskaźnika, który działa tylko w jedną stronę "dobrym".

A 100% łatwo uzyskać, wystarczy pokryć wszystkie ify, nie zastanawiając się nawet przez chwilę, czy takie sytuacje mogą przy rzeczywistym zastosowaniu wystąpić, czy nie, więc patrzenie na procenty jest głupie. To patrzenie na procenty chyba tylko utrudnia ludziom myślenie przy pisaniu testów - bo po co się zastanawiać, skoro sonar pokaże procenty i wymagania spełnione?

0

Zdecydowanie 100%.
Nie można ufać żadnej linijce kodu która nie została przynajmniej raz wykonana. Nie trzeba wszystkiego testować, ale cały kod powinien zostać chociaż raz odpalony, przynajmniej manualnie (ale łatwiej testami).

somekind napisał(a):

No nie wiem, mogą być używane przez jakieś bazujące na refleksji narzędzia typu mapper albo ORM, i już nie będą liczone do statystyk.

Będą liczone jeśli użyjesz tego narzędzia, nie ma znaczenia czy coś bazuje na refleksji czy nie.

somekind napisał(a):

A 100% łatwo uzyskać, wystarczy pokryć wszystkie ify, nie zastanawiając się nawet przez chwilę, czy takie sytuacje mogą przy rzeczywistym zastosowaniu wystąpić, czy nie

Ale po co w ogóle ktoś napisał kod obsługujący sytuacje które nie mogą wystąpić? Kod do wywalenia. Zastanawiać się trzeba przy pisaniu kodu a nie testów

0

Powinno wynosić tyle, ile zespół ustalił i jaką wartość uważa za najlepszy trade-off.

0
Sensacyjny Sebastian napisał(a):

Powinno wynosić tyle, ile zespół ustalił i jaką wartość uważa za najlepszy trade-off.

Ta odpowiedź jest useless, bo wymaga zadania drugiego pytania "Jaką wartość powinien ustalić zespół i jaki jest najlepszy trade-off?". A odpowiedź na nie, skolei wymaga kolejnych pytań.

Nic nie wnosi do tematu.

Weź pod uwagę że zespół ustalając odpowiedni poziom, musiałby się kierować takimi samymi wytycznymi jak pojedynczy programista.

Ps: chyba że chciałeś po prostu napisać odpowiedź "who cares? Coverage może być takie jakie chcesz."

1

Pytający raczej nie oczekuje, że powiesz mu jakąś konkretną wartość. Wolę mieć 5% pokrycia robionego przez dobre testy, niż udawanie, że mam 90% i nawet nie wiem co jest przetestowane, bo wyniki nie zwracają kompletnie żadnej wartościowej informacji. Jak każda metryka wyrażana w procentach, coverage jest jedynie wynikiem dzielenia licznika przez mianownik. Jak masz 300 linii kodu, który coś robi i wrzucisz 3000 linii kodu, który nic nie robi, ale jest "pokryty testami", to dostaniesz pokrycie 91%, które jednoczenie nic nie wniesie.

--edit
Wymyśliłem właśnie czyste zło, w rozwlekłych językach typu Java jest jak wiadomo w piiiip generowanego kodu, metody dostępowe itd. Właściwie to nie jest problemem zrobienie jakiejś nakładki na mockito, która wygeneruje takie testy dynamicznie na zasadzie, że widząc jakiegoś propertisa (set+get), zrobi sobie sama mocka wartości i sprawdzi asercją, czy wartość przekazana w set jest wartością otrzymaną w get. Konstruktory też da się tak ogarnąć.

3

Pytanie pochodzi z rekrutacji.
Sam fakt, że takie pytanie zostało zadanie świadczy o tym, że
a) oczekiwali odpowiedzi, że oczywiście że 100%, a nawet 120% bo jesteśmy ponadprzeciętni
b) oczekiwali odpowiedzi, że Code Coverage nie jest do końca dobrą metryką mierzącą jakość kodu/testów i stanowiło otwarcie do dyskusji

0

Nie koniecznie ktoś zero-jedynkowo patrzy na odpowiedź, 50%/90%/100%, tu raczej chodziło o to żebyś wytłumaczył, a nie podał odpowiedź jak kalkulator 100%.

W większości FAANG rekrutacja tak wygląda, że dostajesz zadanie nawet super proste i nie jesteś w 100%-tach oceniany z tego kodu co napiszesz, ale jak analizujesz problem i jak myślisz, jaki z ciebie engineer.

Na co komu typ, co wyuczył się na pamięć odpowiedzi, taka rozmowa i przemyślenia są więcej warte, bo problem jest zbyt rozmyty.

2

Ja chciałem napisać coś mądrego o tym że 100% i że 100% to dopiero początek, ale spojrzałem że w swoim pet projekcie mam tylko 51% https://helvm.org/helpa/reports/hpc/helpa-test/hpc_index_fun.html XD A to projekt który ma więcej bo w https://helvm.org/helma/reports/hpc/helma-test/hpc_index_fun.html mam 44% XD

Jak do tego doszło? Nie wiem :D Chyba założę sobie issue w gicie na dopisywanie testów :D

1
KamilAdam napisał(a):

Ja chciałem napisać coś mądrego o tym że 100% i że 100% to dopiero początek, ale spojrzałem że w swoim pet projekcie mam tylko 51% https://helvm.org/helpa/reports/hpc/helpa-test/hpc_index_fun.html XD A to projekt który ma więcej bo w https://helvm.org/helma/reports/hpc/helma-test/hpc_index_fun.html mam 44% XD

Jak do tego doszło? Nie wiem :D Chyba założę sobie issue w gicie na dopisywanie testów :D

Ogólnie, jak ktoś osiągnie 100% coverage, to to może być zdrowe coverage albo niezdrowe coverage.

Niektóre frameworki testowe mają coś takiego jak scope'y coverage'ów. Np jak mamy test który testuje powiedzmy wysyłanie maili, i kod który wysyła maile korzysta z modułu userów, ale nasz test w żaden sposób nie testuje modułu userów (po prostu jest używany "przy okazji"), to warto wtedy w testach wyłączyć moduł userów z generowania coverage'a, żeby modół userów nie został przez przypadek oznaczony niepoprawnie jako covered. PhpUnit ma np coś takiego jak @covers, i to nam zawęża generowanie coverega'a do konkretnej funkcji, klasy bądź modułu.

Następnym krokiem mógłby być Path Coverage, Branch coverage, TDD oczywiście, noi potem mutation testing.

Ale to wszystko się sprowadza do tego że coverage nie ma za wiele wspólnego z tym, które/ile kawałki kodu są przetestowane. To że dany kod został wywołany/uruchomiony w teście, nie mówi absolutnie nic o tym czy ten kawałek był przetestowany czy nie.

1
Riddle napisał(a):

Następnym krokiem mógłby być Path Coverage, Branch coverage, TDD oczywiście, noi potem mutation testing.

Path Coverage, Branch coverage tego nie rozumiem XD muszę sprawdzić co to

mutation testing - i jeszcze Property-based Testing. Od prawie samego poczatku o tym myśle :D

TDD - było, chociaż czasem krzywe bo znałem tylko input funkcji a nie zawsze znałem output. No to skąd ten rozstrzał aż tak duży u mnie?

  • Starałem się pisać testy na feature'y biznesowe, przez co czasem zostawały mi nie użyte fragmenty kody
  • czasem potrzebowałem zaimplementować jakiś interfejs (type classe'ę), ale nie użyłem wszystkich metod
  • mam współdzieloną bibliotekę między dwoma projektami. Współdzielenie jest na zasadzie kopiowania kodu (wiem barachło straszne). Jak wydzielę bibliotekę do osobnego pakietu to coverage wzrośnie w projektach (a w bibliotece będzie pewnie na poziomie 10% XD )

Bogowie, czemu doba ma tyko 24h, a ja jeszcze ogródem muszę ogarniąć XD

2
obscurity napisał(a):

Zdecydowanie 100%.
Nie można ufać żadnej linijce kodu która nie została przynajmniej raz wykonana. Nie trzeba wszystkiego testować, ale cały kod powinien zostać chociaż raz odpalony, przynajmniej manualnie (ale łatwiej testami).

Tyle, że linijka została wywołana raz w testach to nie znaczy ani, że raz wystarczy, ani że nawet ten raz została wywołana sensownie.

Będą liczone jeśli użyjesz tego narzędzia, nie ma znaczenia czy coś bazuje na refleksji czy nie.

Ma znaczenie, bo np. kod może być używany, ale nie w testach jednostkowych.

Ale po co w ogóle ktoś napisał kod obsługujący sytuacje które nie mogą wystąpić? Kod do wywalenia.

Chodziło mi o to, że pewne rozgałęzienia kodu mogą być wykonywane po spełnieniu określonych warunków, pewnej kombinacji danych wejściowych. Testowanie dla nierealistycznych kombinacji sprawia, że można osiągnąć 100% pokrycia w kodzie, ale bardzo mało % w realnych przypadkach użycia. Czyli osiągnąć testy, które będą ładnie wyglądały dla kierownika, ale niczego nie będą sprawdzały.

Zastanawiać się trzeba przy pisaniu kodu a nie testów

A bo testy to nie kod?
Strasznie nieprofesjonalne podejście.

0

Odpowiedziałbym, że nie ma jakiejś reguły, ale jeśli muszę podać liczbę to 80%.

Ogólnie te 100% to da się uzyskać przez pisanie jakichś bezsensownych testów. Np. dosyć często widzę, że nie jest testowany toString() w Javie - i nie widzę nic złego w tym, że czegoś takiego nie ma. Dodatkowo często i gęsto wszystko wywoływane w javowym main nie jest testowane - bo po prostu nie widzę korzyści z testowania czegoś takiego:

public class Main {
   private final static Logger LOG = LoggerManager.getLogger(Main.class);

   // Ta metoda nigdy nie będzie zawołana w testach
   public static void main(final String[] args) {
      if (LOG.isDebug()) {
         printAvailableJdbcDrivers(); 
      }

      // ApplicationConfig, ApplicationBuilder i Application są już przetestowane
      ApplicationConfig config = ApplicationConfig.fromArgs(args);
      Application app = new ApplicationBuilder(config).build();
      app.start();
   }

   // Ta metoda nigdy nie będzie przetestowana
   private static void printAvailableJdbcDrivers() {
      // ...
   }
}

Jasne, dało by się sklecić klasę

0
wartek01 napisał(a):

Ogólnie te 100% to da się uzyskać przez pisanie jakichś bezsensownych testów. Np. dosyć często widzę, że nie jest testowany toString() w Javie - i nie widzę nic złego w tym, że czegoś takiego nie ma. Dodatkowo często i gęsto wszystko wywoływane w javowym main nie jest testowane - bo po prostu nie widzę korzyści z testowania czegoś takiego:

No korzyść jest taka, że jeśli ktoś popełni błąd w jednej z tych funkcji, to Test ją znajdzie. Też nie jest jakiś trudno napisać test pod te funkcje.

3
Riddle napisał(a):

No korzyść jest taka, że jeśli ktoś popełni błąd w jednej z tych funkcji, to Test ją znajdzie. Też nie jest jakiś trudno napisać test pod te funkcje.

  1. W przypadku toString to, co przetestujesz to to, czy generator metody toString zadziałał. Co więcej, sama funkcja ma często i gęsto niewielkie znaczenie przy funkcjonalności, więc nawet jeśli implementacja będzie wyglądała tak: return "AAA"; to aplikacja i tak będzie działała poprawnie z punktu widzenia użytkownika.
  2. W przypadku metody Main.main napisanie testu to wcale nie taka prosta sprawa - zwłaszcza, jeśli metody Application.start() oraz ApplicationBuilder.build() są nietrywialne i np. wymagają zależności zewnętrznych.
0
wartek01 napisał(a):
  1. W przypadku toString to, co przetestujesz to to, czy generator metody toString zadziałał. Co więcej, sama funkcja ma często i gęsto niewielkie znaczenie przy funkcjonalności, więc nawet jeśli implementacja będzie wyglądała tak: return "AAA"; to aplikacja i tak będzie działała poprawnie z punktu widzenia użytkownika.

To po co w ogóle ta metoda? Strzelam że do developmentu? Ja bym ją wywalił i używał debuggera, bo inaczej to jest dead code. Chyba, że jest jakiejś miejsce w którym user ją widzi, wtedy trzeba ją otestować.

  1. W przypadku metody Main.main napisanie testu to wcale nie taka prosta sprawa - zwłaszcza, jeśli metody Application.start() oraz ApplicationBuilder.build() są nietrywialne i np. wymagają zależności zewnętrznych.

No ale napisanie automatycznego testu pod to i tak zajmie mniej czasu niz testowanie tego manualnie.

2
Riddle napisał(a):

To po co w ogóle ta metoda? Strzelam że do developmentu? Ja bym ją wywalił i używał debuggera, bo inaczej to jest dead code.

Logów nie czytasz? ja czesto miałem w Javie w kodzie logi w rodzaju:

logger.info("params: " + params);

tylko żeby to zadziałało trzeba zaimplementować toString()

1 użytkowników online, w tym zalogowanych: 0, gości: 1