Określanie przydatnego testu jednostkowego


47

Przeglądałem dokumenty phpunit i natrafiłem na następujący cytat:

Zawsze możesz napisać więcej testów. Jednak szybko przekonasz się, że tylko ułamek testów, które możesz sobie wyobrazić, są w rzeczywistości przydatne. To, co chcesz, to pisać testy, które zawiodą, nawet jeśli uważasz, że powinny one działać, lub testy, które zakończą się powodzeniem, nawet jeśli uważasz, że powinny zawieść. Innym sposobem myślenia o tym jest stosunek kosztów do korzyści. Chcesz napisać testy, które zwrócą ci informacje. - Bogata gamma

Zastanawiałem się. W jaki sposób określasz, co sprawia, że ​​test jednostkowy jest bardziej przydatny niż inny, oprócz tego, co podano w cytacie na temat kosztów / korzyści. Jak decydujesz, dla którego fragmentu kodu tworzysz testy jednostkowe? Proszę o to, ponieważ inny z tych cytatów również powiedział:

Więc jeśli nie chodzi o testowanie, o co chodzi? Chodzi o to, aby dowiedzieć się, co próbujesz zrobić, zanim uciekniesz na wpół, aby spróbować. Piszesz specyfikację, która precyzuje mały aspekt zachowania w zwięzłej, jednoznacznej i wykonywalnej formie. To takie proste. Czy to oznacza, że ​​piszesz testy? Nie. Oznacza to, że piszesz specyfikacje tego, co twój kod będzie musiał zrobić. Oznacza to, że z góry określasz zachowanie swojego kodu. Ale niewiele wcześniej. W rzeczywistości tuż przed napisaniem kodu najlepiej jest, ponieważ wtedy masz pod ręką tyle informacji, ile potrzebujesz. Podobnie jak dobrze zrobione TDD, pracujesz w małych krokach ... określając jeden mały aspekt zachowania na raz, a następnie wdrażając go. Kiedy zdasz sobie sprawę, że chodzi o określenie zachowania, a nie pisanie testów, twój punkt widzenia się zmienia. Nagle pomysł posiadania klasy testowej dla każdej klasy produkcyjnej jest absurdalnie ograniczający. Myśl o przetestowaniu każdej z twoich metod za pomocą własnej metody testowej (w relacji 1-1) będzie śmieszna. - Dave Astels

Ważną częścią tego jest

* A myśl o przetestowaniu każdej z twoich metod za pomocą własnej metody testowej (w relacji 1-1) będzie śmieszna. *

Jeśli więc utworzenie testu dla każdej metody jest „śmiechu warte”, to w jaki sposób / kiedy wybierasz, dla czego piszesz testy?


5
To dobre pytanie, ale myślę, że jest to pytanie niezależne od języka programowania - dlaczego oznaczyłeś go php?

Oto kilka naprawdę dobrych artykułów, które dały mi dobry kierunek na temat testów jednostkowych, które powinienem pisać i dla jakich obszarów należy przeprowadzić testy automatyczne. - blog.stevensanderson.com/2009/11/04/… - blog.stevensanderson.com/2009/08/24/… - ayende.com/blog/4218/scenario-driven-tests
Kane

Odpowiedzi:


27

Ile testów na metodę?

Cóż, teoretycznym i wysoce niepraktycznym maksimum jest złożoność ścieżki N (załóżmy, że wszystkie testy obejmują różne sposoby w kodzie;)). Minimum to JEDEN !. Według metody publicznej , czyli nie testuje szczegółów implementacji, tylko zewnętrzne zachowania klasy (zwracają wartości i wywołują inne obiekty).

Cytujesz:

* A myśl o przetestowaniu każdej z twoich metod za pomocą własnej metody testowej (w relacji 1-1) będzie śmieszna. *

a następnie zapytaj:

Jeśli więc utworzenie testu dla każdej metody jest „śmiechu warte”, to w jaki sposób / kiedy wybierasz, dla czego piszesz testy?

Ale myślę, że źle zrozumiałeś tutaj autora:

Idea posiadania one test methodper one method in the class to testjest tym, co autor nazywa „śmiesznym”.

(Dla mnie przynajmniej) Nie chodzi o „mniej”, chodzi o „więcej”

Pozwólcie, że sformułuję to tak, jakbym go rozumiał:

Myśl o przetestowaniu każdej z metod przy użyciu TYLKO JEDNEJ METODY (własnej metody testowej w relacji 1-1) będzie śmieszna.

Aby ponownie zacytować ofertę:

Kiedy zdasz sobie sprawę, że chodzi o określenie zachowania, a nie pisanie testów, twój punkt widzenia się zmienia.


Kiedy ćwiczysz TDD, nie myślisz :

Mam metodę calculateX($a, $b);i potrzebuje testu, testCalculcateXktóry przetestuje WSZYSTKO na temat tej metody.

TDD mówi ci, aby pomyśleć o tym, JAK POWINIEN WIDZIĆ Twój kod :

Muszę obliczyć większą z dwóch wartości ( pierwszy przypadek testowy! ), Ale jeśli $ a jest mniejsze od zera, to powinien wygenerować błąd ( drugi przypadek testowy! ), A jeśli $ b jest mniejszy od zera, powinien .... ( trzeci przypadek testowy! ) i tak dalej.


Chcesz przetestować zachowania, a nie tylko pojedyncze metody bez kontekstu.

W ten sposób otrzymujesz zestaw testowy, który jest dokumentacją twojego kodu i NAPRAWDĘ wyjaśnia, co należy zrobić, a może nawet dlaczego :)


Jak decydujesz, dla którego fragmentu kodu tworzysz testy jednostkowe?

Cóż, wszystko, co kończy się w repozytorium lub gdziekolwiek w pobliżu produkcji, wymaga testu. Nie sądzę, żeby autor twoich cytatów nie zgodził się z tym, jak starałem się stwierdzić powyżej.

Jeśli nie masz testu, zmiana kodu staje się znacznie trudniejsza (droższa), zwłaszcza jeśli nie dokonujesz zmiany.

TDD jest sposobem na upewnienie się, że masz testy na WSZYSTKO, ale tak długo, jak PISUJESZ testy, wszystko jest w porządku. Zwykle pisanie ich tego samego dnia pomaga, ponieważ nie zamierzasz tego robić później, prawda? :)



Odpowiedź na komentarze:

przyzwoita liczba metod nie może zostać przetestowana w określonym kontekście, ponieważ albo zależą, albo są zależne od innych metod

Są trzy rzeczy, które te metody mogą wywołać:

Metody publiczne innych klas

Możemy wyśmiewać inne klasy, więc zdefiniowaliśmy tam stan. Kontrolujemy kontekst, więc nie stanowi to problemu.

* Metody chronione lub prywatne na tym samym *

Wszystko, co nie jest częścią publicznego interfejsu API klasy, zwykle nie jest testowane bezpośrednio.

Chcesz przetestować zachowanie, a nie implementację, a jeśli klasa robi wszystko, działa w jednej dużej publicznej metodzie lub w wielu mniejszych chronionych metodach, które wywoływane są implementacją . Chcesz móc ZMIENIĆ te chronione metody BEZ dotykania swoich testów. Ponieważ testy zostaną przerwane, jeśli zmiany w kodzie zmienią zachowanie! Właśnie po to są twoje testy, żeby ci powiedzieć, kiedy coś złamiesz :)

Metody publiczne w tej samej klasie

To się nie zdarza często, prawda? A jeśli tak jest w poniższym przykładzie, istnieje kilka sposobów radzenia sobie z tym:

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

To, że setery istnieją i nie są częścią podpisu metody wykonania, to inny temat;)

Możemy tutaj przetestować, czy wykonanie wykonuje wysadzenie, gdy ustawimy nieprawidłowe wartości. To setBlarzuca wyjątek, gdy przekazujesz ciąg, który można przetestować osobno, ale jeśli chcemy przetestować, czy te dwie dozwolone wartości (12 i 14) nie działają RAZEM (z jakiegokolwiek powodu) niż jeden przypadek testowy.

Jeśli chcesz mieć „dobry” pakiet testowy, możesz, w php, może (!) Dodać @covers Stuff::executeadnotację, aby upewnić się, że generujesz pokrycie kodu tylko dla tej metody, a inne rzeczy, które są po prostu konfigurowane, muszą zostać przetestowane osobno (ponownie, jeśli chcesz tego).

Chodzi o to, że może najpierw musisz stworzyć otaczający świat, ale powinieneś być w stanie napisać sensowne przypadki testowe, które zwykle obejmują tylko jedną lub dwie rzeczywiste funkcje (setery nie liczą się tutaj). Resztę można wykpić z eteru lub najpierw przetestować, a następnie polegać na niej (patrz @depends)


* Uwaga: Pytanie zostało migrowane z SO i początkowo dotyczyło PHP / PHPUnit, dlatego przykładowy kod i referencje pochodzą ze świata php, myślę, że dotyczy to również innych języków, ponieważ phpunit nie różni się tak bardzo od innych xUnit ramy testowe.


Bardzo szczegółowe i pouczające ... Powiedziałeś: „Chcesz przetestować zachowania, a nie tylko pojedyncze metody bez kontekstu”. Z pewnością nie można przetestować przyzwoitej liczby metod w określonym kontekście, ponieważ są one zależne lub zależne od innych metod , dlatego przydatny warunek testowy byłby kontekstowy tylko wtedy, gdyby testowano również osoby zależne? Czy też źle interpretuję, co masz na myśli>
zcourts

@ robinsonc494
Zredaguję

dzięki za zmiany i przykład, to z pewnością pomaga. Myślę, że moim nieporozumieniem (jeśli można to tak nazwać) było to, że chociaż czytałem o testowaniu pod kątem „zachowania”, w jakiś sposób po prostu (chyba?) Myślałem o przypadkach testowych, które koncentrowały się na implementacji.
zcourts

@ robinsonc494 Być może pomyśl o tym w ten sposób: jeśli uderzysz kogoś, kto eterem uderzy, zadzwoń po policję lub uciekniesz. To zachowanie. To właśnie robi Osoba. Fakt, że wykorzystuje małże wywołane przez małe ładunki elektryczne z jego mózgu, jest implementacją. Jeśli chcesz przetestować czyjąś reakcję, uderz go i sprawdź, czy zachowuje się tak, jak się tego spodziewasz. Nie wkładasz go do skanera mózgu i nie sprawdzasz, czy impulsy są wysyłane do małży. Niektórzy
chodzą

3
Myślę, że najlepszym przykładem, jaki widziałem TDD, który naprawdę pomógł mu napisać, jak napisać przypadki testowe, była gra w kręgle Kata autorstwa wujka Boba Martina. slideshare.net/lalitkale/bowling-game-kata-by-robert-c-martin
Amy Anuszewski

2

Testy i testy jednostkowe to nie to samo. Testowanie jednostkowe jest bardzo ważnym i interesującym podzbiorem Testowania ogólnie. Twierdzę, że skupienie się na testowaniu jednostkowym skłania do myślenia o tego rodzaju testowaniu w sposób, który w pewien sposób jest sprzeczny z powyższymi cytatami.

Po pierwsze, jeśli podążamy za TDD, a nawet DTH (tj. Opracowujemy i testujemy w ścisłej harmonii), używamy testów, które piszemy, aby skupić się na poprawnym zaprojektowaniu. Myśląc o przypadkach narożnych i odpowiednio pisząc testy, unikamy błędów, więc w rzeczywistości piszemy test, który spodziewamy się zaliczenia (OK na początku TDD zawodzą, ale to tylko artefakt zamawiania, kiedy kod jest gotowy, oczekujemy, że przejdą, a większość tak, ponieważ myślałeś o kodzie.

Po drugie, testy jednostkowe naprawdę sprawdzają się, gdy dokonujemy refaktoryzacji. Zmieniamy naszą implementację, ale oczekujemy, że odpowiedzi pozostaną takie same - Test jednostkowy stanowi naszą ochronę przed zerwaniem umowy dotyczącej interfejsu. Po raz kolejny oczekujemy, że testy zakończą się pomyślnie.

Oznacza to, że dla naszego interfejsu publicznego, który prawdopodobnie jest stabilny, potrzebujemy wyraźnej identyfikowalności, abyśmy mogli zobaczyć, że każda metoda publiczna jest testowana.

Aby odpowiedzieć na Twoje wyraźne pytanie: Test jednostkowy interfejsu publicznego ma wartość.

edytowane w komentarzu w odpowiedzi:

Testujesz prywatne metody? Tak, powinniśmy, ale jeśli nie musimy czegoś testować, to właśnie tam pójdę na kompromis. W końcu, jeśli publiczne metody działają, to czy te błędy w prywatnych materiałach mogą być tak ważne? Pragmatycznie, rezygnacja ma miejsce w prywatnych sprawach, ciężko pracujesz, aby utrzymać swój publiczny interfejs, ale jeśli rzeczy, na których polegasz, mogą się zmienić. W pewnym momencie utrzymanie wewnętrznych testów może być dużym wysiłkiem. Czy ten wysiłek jest dobrze wykorzystany?


Aby się upewnić, że rozumiem, co mówisz: podczas testowania skoncentruj się na testowaniu interfejsu publicznego, prawda? Zakładając, że jest to poprawne ... czy nie ma większej możliwości pozostawienia błędów w prywatnych metodach / interfejsach, dla których nie przeprowadzano testów jednostkowych, niektóre trudne błędy w niesprawdzonym „prywatnym” interfejsie mogłyby doprowadzić do pozytywnego wyniku testu naprawdę powinienem zawieść. Czy tak się mylę?
zcourts

Za pomocą pokrycia kodu możesz stwierdzić, kiedy kod w twoich metodach prywatnych nie jest wykonywany podczas testowania metod publicznych. Jeśli twoje metody publiczne są w pełni objęte, wszelkie nieosłonięte metody prywatne są oczywiście nieużywane i można je usunąć. Jeśli nie, potrzebujesz więcej testów dla swoich publicznych metod.
David Harkness,

2

Testy jednostkowe powinny być częścią większej strategii testowania. Przestrzegam tych zasad, wybierając rodzaje testów do napisania i kiedy:

  • Skoncentruj się na pisaniu kompleksowych testów. Obejmujesz więcej kodu na test niż w testach jednostkowych, dzięki czemu zyskujesz więcej testowania za grosze. Spraw, by były to automatyczne sprawdzanie poprawności systemu jako całości.

  • Przejdź do pisania testów jednostkowych wokół modeli użytkowych o skomplikowanej logice. Testy jednostkowe są warte swojej wagi w sytuacjach, w których kompleksowe testy byłyby trudne do debugowania lub nieporęczne do napisania w celu zapewnienia odpowiedniego pokrycia kodu.

  • Poczekaj, aż testowany interfejs API będzie stabilny, aby napisać test dowolnego typu. Chcesz uniknąć konieczności refaktoryzacji zarówno implementacji, jak i testów.

Rob Ashton ma świetny artykuł na ten temat, z którego mocno zaczerpnąłem, aby sformułować powyższe zasady.


+1 - niekoniecznie zgadzam się ze wszystkimi twoimi punktami (lub punktami artykułu), ale zgadzam się, że większość testów jednostkowych jest bezużyteczna, jeśli jest wykonywana na ślepo (podejście TDD). Są one jednak niezwykle cenne, jeśli jesteś mądry w podejmowaniu decyzji, na co warto poświęcić czas na przeprowadzanie testów jednostkowych. Zgadzam się całkowicie, że masz dużo, dużo więcej do roboty przy pisaniu testów wyższego poziomu, w szczególności testów automatycznych na poziomie podsystemu. Problem z kompleksowymi zautomatyzowanymi testami polega na tym, że byłyby one trudne, jeśli nie całkowicie niepraktyczne, dla systemu, który ma jakąkolwiek wielkość / złożoność.
Dunk

0

Zazwyczaj stosuję inne podejście do testów jednostkowych, które wydaje się działać dobrze. Zamiast myśleć o testach jednostkowych jako o „Testowaniu niektórych zachowań”, myślę o tym bardziej jako o „specyfikacji, której musi przestrzegać mój kod”. W ten sposób możesz w zasadzie zadeklarować, że obiekt powinien zachowywać się w określony sposób, a biorąc pod uwagę, że zakładasz, że gdzie indziej w twoim programie, możesz być całkiem pewien, że jest stosunkowo wolny od błędów.

Jeśli piszesz publiczny interfejs API, jest to niezwykle cenne. Jednak zawsze będziesz potrzebować dobrej dawki kompleksowych testów integracyjnych, ponieważ podejście do 100% pokrycia testów jednostkowych zwykle nie jest tego warte i będzie brakowało rzeczy, które większość osób uznałaby za „niesprawdzalne” metodami testów jednostkowych (kpiny, itp)

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.