Czy pokrycie testowe jest odpowiednią miarą jakości kodu?


20

Jeśli mam jakiś kod, który ma 80% pokrycia testowego (wszystkie testy przeszły pomyślnie), czy można uczciwie powiedzieć, że jest on wyższej jakości niż kod bez pokrycia testowego?

A może uczciwie jest powiedzieć, że jest łatwiejsza w utrzymaniu?


2
100% zasięgu nie oznacza, że ​​został dobrze przetestowany. Ale 0% oznacza, że ​​wcale nie został przetestowany.
mouviciel

1
Technicznie nie. Praktycznie tak. Doświadczenie nauczyło wielu inżynierów oprogramowania i testerów, że gdy pokrycie kodu osiągnie około 80%, rodzaje defektów, dla których testy jednostkowe są odpowiednie, zaczynają się niwelować. To zasada pareto. Zasadniczo po przejściu do punktu, w którym pokrywasz 80% kodu, niezależnie od jakości testów, prawdopodobnie przetestowałeś 20% kodu, który powoduje dość dokładnie większość potencjalnych problemów. To nie jest absolutna, lecz konwencjonalna mądrość. Musisz być bardziej dokładny, jeśli życie zależy od twoich testów.
Calphool,

@JoeRounceville Nie jestem pewien ... Mogę osiągnąć wysoki zasięg testu, nie testując niczego naprawdę przydatnego. Pokrycie mówi jedynie, ile kodu jest dotykane przez zestaw testów, a nie czy testy są znaczące.
Andres F.

1
@AndresF. Dlatego powiedziałem „technicznie nie, praktycznie tak”. Ludzie nie są idiotami (ogólnie). Nie testują (generalnie) tylko przypadków bez myślenia. Opierając się na doświadczeniu , wiele sklepów zatrzymuje się w około 80% zasięgu, przyjmując (dość bezpieczne) założenie, że ich ludzie nie są kretynami.
Calphool

Odpowiedzi:


24

W ścisłym tego słowa znaczeniu nie można wysuwać żadnych roszczeń, dopóki nie zostanie ustalona jakość zestawu testów. Zaliczenie 100% testów nie ma znaczenia, jeśli większość testów jest trywialna lub powtarzalna.

Pytanie brzmi: czy w historii projektu którykolwiek z testów wykrył błędy? Celem testu jest znalezienie błędów. A jeśli nie, to nie zdały egzaminu. Zamiast poprawiać jakość kodu, mogą jedynie dawać fałszywe poczucie bezpieczeństwa.

Aby ulepszyć swoje projekty testowe, możesz użyć (1) technik whitebox, (2) technik blackbox i (3) testów mutacji.

(1) Oto kilka dobrych technik whitebox do zastosowania w projektach testowych. Test białej skrzynki jest konstruowany z uwzględnieniem konkretnego kodu źródłowego. Jednym z ważnych aspektów testów whitebox jest pokrycie kodu:

  • Czy wywoływana jest każda funkcja? [Zasięg funkcjonalny]
  • Czy każde polecenie jest wykonywane? [Pokrycie wyciągiem - zarówno pokrycie funkcjonalne, jak i pokrycie wyciągu są bardzo podstawowe, ale lepsze niż nic]
  • Czy dla każdej decyzji (takiej jak iflub while) masz test, który wymusza, aby był prawdziwy, a drugi, który wymusza, aby był fałszywy? [Zakres decyzji]
  • Czy dla każdego warunku, który jest koniunkcją (zastosowaniami &&) lub rozłączeniem (zastosowaniami ||), każde podwyrażenie ma test, w którym jest to prawda / fałsz? [Pokrycie warunków]
  • Pokrycie w pętli: Czy masz test, który wymusza 0 iteracji, 1 iterację, 2 iteracje?
  • Czy każdy breakz nich obejmuje pętlę?

(2) Techniki Blackbox są stosowane, gdy wymagania są dostępne, ale sam kod nie jest. Może to prowadzić do wysokiej jakości testów:

  • Czy Twoje testy Blackbox obejmują wiele celów testowych? Chcesz, aby twoje testy były „tłuste”: nie tylko testują one funkcję X, ale także testują Y i Z. Interakcja między różnymi funkcjami to świetny sposób na znalezienie błędów.
  • Jedynym przypadkiem, w którym nie chcesz testów „grubych”, jest testowanie warunku błędu. Na przykład testowanie pod kątem nieprawidłowych danych wejściowych użytkownika. Jeśli próbujesz osiągnąć wiele nieprawidłowych celów testowania danych wejściowych (na przykład nieprawidłowy kod pocztowy i nieprawidłowy adres ulicy), prawdopodobne jest, że jeden przypadek maskuje drugi.
  • Rozważ typy danych wejściowych i utwórz „klasę równoważności” dla typów danych wejściowych. Na przykład, jeśli Twój kod sprawdza, czy trójkąt jest równoboczny, test wykorzystujący trójkąt o bokach (1, 1, 1) prawdopodobnie wykryje te same rodzaje błędów, co dane testowe (2, 2, 2) i (3, 3, 3) znajdzie. Lepiej poświęcić czas na myślenie o innych klasach danych wejściowych. Na przykład, jeśli twój program obsługuje podatki, będziesz potrzebować testu dla każdej grupy podatkowej. [Nazywa się to partycjonowaniem równoważności.]
  • Przypadki szczególne są często związane z wadami. Dane testowe powinny również mieć wartości graniczne, takie jak te na, powyżej lub poniżej krawędzi zadania równoważności. Na przykład testując algorytm sortowania, będziesz chciał przetestować pustą tablicę, tablicę z jednym elementem, tablicę z dwoma elementami, a następnie tablicę bardzo dużą. Należy rozważyć przypadki graniczne nie tylko dla danych wejściowych, ale także dla danych wyjściowych. [To jest analiza wartości granicznej wywołania.]
  • Inną techniką jest „zgadywanie błędów”. Czy masz wrażenie, że wypróbujesz specjalną kombinację, która sprawi, że Twój program się zepsuje? Więc po prostu spróbuj! Pamiętaj: Twoim celem jest znalezienie błędów, a nie potwierdzenie, że program jest prawidłowy . Niektórzy ludzie potrafią zgadywać błędy.

(3) Na koniec załóżmy, że masz już wiele fajnych testów zasięgu whitebox i zastosowałeś techniki blackbox. Co jeszcze możesz zrobić? Czas przetestować swoje testy . Jedną z technik, której można użyć, jest testowanie mutacji.

Podczas testowania mutacji dokonujesz modyfikacji (kopii) swojego programu, mając nadzieję na stworzenie błędu. Mutacją może być:

Zmień odniesienie jednej zmiennej na inną zmienną; Wstaw funkcję abs (); Zmień mniej niż na większą niż; Usuń oświadczenie; Zamień zmienną na stałą; Usuń metodę zastępującą; Usuń odwołanie do super metody; Zmień kolejność argumentów

Stwórz kilkadziesiąt mutantów w różnych miejscach swojego programu [program będzie nadal musiał się skompilować, aby przetestować]. Jeśli twoje testy nie wykryją tych błędów, musisz teraz napisać test, który może znaleźć błąd w zmutowanej wersji twojego programu. Gdy test znajdzie błąd, zabiłeś mutanta i możesz spróbować innego.


Dodatek : Zapomniałem wspomnieć o tym efekcie: Błędy mają tendencję do skupiania się . Oznacza to, że im więcej błędów znajdziesz w jednym module, tym większe prawdopodobieństwo, że znajdziesz więcej błędów. Tak więc, jeśli test się nie powiedzie (to znaczy test się powiedzie, ponieważ celem jest znalezienie błędów), należy nie tylko naprawić błąd, ale także napisać więcej testów dla modułu, używając powyższe techniki.

Dopóki znajdziesz błędy w stałym tempie, działania testowe muszą być kontynuowane. Tylko w przypadku spadku liczby wykrytych błędów możesz mieć pewność, że podjąłeś starania w zakresie testowania na tym etapie rozwoju.


7

Według jednej definicji jest łatwiejsza w utrzymaniu, ponieważ każda przełamująca zmiana jest bardziej prawdopodobne, że zostanie przechwycona przez testy.

Jednak fakt, że kod przechodzi testy jednostkowe, nie oznacza, że ​​jest on z natury wyższej jakości. Kod może nadal być źle sformatowany z nieistotnymi komentarzami i nieodpowiednimi strukturami danych, ale nadal może przejść testy.

Wiem, który kod wolałbym zachować i rozszerzyć.


7

Kod bez żadnych testów może być wyjątkowo wysokiej jakości, czytelny, piękny i wydajny (lub całkowicie śmieciowy), więc nie, nie jest uczciwe twierdzenie, że kod z 80% pokryciem testowym jest wyższej jakości niż kod bez pokrycia testowego.

Można śmiało powiedzieć, że kod w 80% objęty dobrymi testami jest prawdopodobnie akceptowalnej jakości i prawdopodobnie stosunkowo łatwy do utrzymania. Ale tak naprawdę gwarantuje niewiele.


3

Nazwałbym to bardziej refaktowalnym. Refaktoryzacja staje się niezwykle łatwa, jeśli kod jest objęty wieloma testami.

Byłoby sprawiedliwie nazwać to łatwiejszym w utrzymaniu.


2

Zgodziłbym się co do części, którą można utrzymać. Michael Feathers niedawno opublikował film z doskonałej wypowiedzi o swojej nazwie „ Głęboka synergia między testowalnością a dobrym projektem ”, w której omawia ten temat. W rozmowie mówi, że związek jest jednym ze sposobów, to znaczy, że dobrze zaprojektowany kod jest testowalny, ale kod testowany niekoniecznie jest dobrze zaprojektowany.

Warto zauważyć, że streaming wideo nie jest świetny w filmie, więc warto pobrać, jeśli chcesz oglądać w całości.


-2

Od pewnego czasu zadaję sobie to pytanie w związku z „ochroną warunków”. Co powiesz na tę stronę z atollic.com „Dlaczego analiza zasięgu kodu?”

Mówiąc bardziej technicznie, analiza pokrycia kodu znajduje obszary w twoim programie, które nie są objęte twoimi przypadkami testowymi, umożliwiając ci tworzenie dodatkowych testów, które obejmowałyby inaczej nie przetestowane części twojego programu. Dlatego ważne jest, aby zrozumieć, że pokrycie kodu pomaga zrozumieć jakość procedur testowych, a nie jakość samego kodu .

Wydaje się, że jest to tutaj dość istotne. Jeśli masz zestaw przypadków testowych, którym udało się osiągnąć określony poziom pokrycia (kodu lub w inny sposób), prawdopodobnie wywołujesz testowany kod z dość wyczerpującym zestawem wartości wejściowych! Nie powie ci to wiele o testowanym kodzie (chyba że kod wysadzi lub wygeneruje wykrywalne błędy), ale da ci pewność co do zestawu przypadków testowych .

W interesującej zmianie widoku Necker Cube kod testowy jest teraz testowany przez testowany kod!


-3

Istnieje wiele sposobów, aby zagwarantować, że program zrobi to, co zamierzasz, i upewnić się, że modyfikacje nie przyniosą niezamierzonych efektów.

Testowanie jest jednym. Kolejnym jest unikanie mutacji danych. Podobnie jest z systemem typów. Lub formalna weryfikacja.

Tak więc, chociaż zgadzam się, że testowanie jest ogólnie dobrą rzeczą, dany procent testów może nie mieć większego znaczenia. Wolę polegać na czymś napisanym w Haskell bez testów niż na dobrze przetestowanej bibliotece PHP


czy to tylko Twoja opinia, czy możesz jakoś to zrobić?
komar

2
Testowanie nie jest sposobem na zagwarantowanie, że program zrobi to, co zamierzasz.
Andres F.

1
Zastanawiam się, czym jest testowanie
Andrea

@gnat to oczywiście moja opinia. Mimo to mówi to, co mówi. Wziąłem Haskell jako przykład języka, którego kompilator jest bardzo ścisły i daje wiele gwarancji dotyczących dobrze uformowanego wejścia, typów, skutków ubocznych, mutacji danych. Wziąłem PHP jako przykład języka, którego tłumacz jest bardzo łagodny i który nawet nie ma specyfikacji. Nawet przy braku testów obecność wszystkich gwarancji z systemu typów i efektów daje zwykle przyzwoity stopień niezawodności. Aby to zrekompensować testami, trzeba mieć bardzo obszerny pakiet
Andrea

Być może trochę się spieszyłem, kiedy pisałem - rozmawiałem przez telefon - ale nadal uważam, że jest sens. Nie chcę krytykować PHP, ale myślę, że powiedzenie, że w porównaniu Haskell daje znacznie większy stopień niezawodności, jest obiektywnym stwierdzeniem
Andrea
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.