Z wartościowego punktu widzenia w mojej praktyce widzę dwie grupy testów jednostkowych:
- Testy, które testują nietrywialną logikę. Napisanie ich (przed wdrożeniem lub po nim) ujawnia pewne problemy / potencjalne błędy i pomaga zachować pewność w przypadku zmiany logiki w przyszłości.
- Testy testujące bardzo trywialną logikę. Testy te bardziej przypominają kod dokumentu (zwykle z próbą) niż testowanie. Proces obsługi tych testów nie polega na tym, że „zmieniono logikę, test stał się czerwony - dzięki Bogu napisałem ten test”, ale „zmieniono jakiś trywialny kod, test stał się fałszywie ujemny - muszę utrzymać (przepisać) test bez żadnego zysku” . Przez większość czasu testy te nie są warte utrzymywania (z wyjątkiem powodów religijnych). I zgodnie z moim doświadczeniem w wielu systemach testy te stanowią około 80% wszystkich testów.
Próbuję dowiedzieć się, co myślą inni faceci na temat rozdziału testów jednostkowych według wartości i jak to odpowiada mojej separacji. Ale to, co przede wszystkim widzę, to albo propaganda TDD w pełnym wymiarze godzin, albo propaganda testów są bezużyteczni, wystarczy napisać kod. Interesuje mnie coś pośrodku. Mile widziane są twoje przemyślenia lub odniesienia do artykułów / artykułów / książek.
unit-testing
tdd
SiberianGuy
źródło
źródło
Odpowiedzi:
Myślę, że to naturalne, że w testach jednostkowych napotyka się podział. Istnieje wiele różnych opinii na temat tego, jak zrobić to właściwie i oczywiście wszystkie inne opinie są z natury błędne . Ostatnio jest sporo artykułów na temat DrDobbs, które badają ten problem, do którego odsyłam na końcu mojej odpowiedzi.
Pierwszym problemem, jaki widzę w testach, jest to, że łatwo je pomylić. W mojej klasie C ++ w college'u byliśmy narażeni na testy jednostkowe w pierwszym i drugim semestrze. W żadnym z semestrów nic nie wiedzieliśmy o programowaniu - staraliśmy się poznać podstawy programowania przez C ++. Teraz wyobraź sobie, że mówisz uczniom: „O, hej, napisałeś mały roczny kalkulator podatkowy! Teraz napisz kilka testów jednostkowych, aby upewnić się, że działa poprawnie”. Wyniki powinny być oczywiste - wszystkie były okropne, w tym moje próby.
Gdy przyznasz, że nie lubisz pisać testów jednostkowych i chcesz się poprawić, wkrótce staniesz przed modnymi stylami testowania lub różnymi metodami. Testując metodologie, odnoszę się do praktyk takich jak test-first lub do tego, co robi Andrew Binstock z DrDobbs, czyli do pisania testów obok kodu. Oba mają swoje zalety i wady, a ja nie chcę wchodzić w żadne subiektywne szczegóły, ponieważ spowoduje to wojnę z płomieniami. Jeśli nie masz wątpliwości co do tego, która metodologia programowania jest lepsza, być może styl testowania wystarczy. Czy powinieneś używać TDD, BDD, testów opartych na właściwościach? JUnit ma zaawansowane koncepcje zwane teoriami, które zacierają granicę między TDD a testowaniem opartym na właściwościach. Którego użyć, kiedy?
tl; dr Łatwo popełnisz błąd w testowaniu, jest on niezwykle opiniotwórczy i nie sądzę, aby jakakolwiek jedna metodologia testowania była z natury lepsza, o ile są one stosowane rzetelnie i profesjonalnie w kontekście, w którym są odpowiednie. Ponadto testowanie jest moim zdaniem rozszerzenie do twierdzeń lub testów poczytalności, które służyło zapewnieniu szybkiego i bezproblemowego podejścia do rozwoju, które jest teraz o wiele, wiele łatwiejsze.
Dla subiektywnej opinii wolę pisać „fazy” testów, z powodu braku lepszego wyrażenia. Piszę testy jednostkowe, które testują klasy w izolacji, używając w razie potrzeby próbnych. Prawdopodobnie zostaną one wykonane przy użyciu JUnit lub czegoś podobnego. Następnie piszę testy integracyjne lub akceptacyjne, które są uruchamiane osobno i zwykle tylko kilka razy dziennie. To są twoje nietrywialne przypadki użycia. Zwykle używam BDD, ponieważ miło jest wyrażać funkcje w języku naturalnym, czego JUnit nie może łatwo zapewnić.
Wreszcie zasoby. Przedstawią one sprzeczne opinie skupione głównie na testach jednostkowych w różnych językach i różnych ramach. Powinny one przedstawiać podział na ideologię i metodologię, pozwalając jednocześnie na podejmowanie własnych decyzji, o ile nie manipulowałem już zbytnio twoimi opiniami :)
[1] The Corruption of Agile autorstwa Andrew Binstocka
[2] Odpowiedź na odpowiedzi z poprzedniego artykułu
[3] Odpowiedź na zepsucie Agile przez wujka Boba
[4] Odpowiedź na Corruption of Agile Roba Myersa
[5] Po co zawracać sobie głowę testowaniem ogórków?
[6] Cuking to źle
[7] Krok od narzędzi
[8] Komentarz do „Liczb rzymskich Kata z komentarzem”
[9] Cyfry rzymskie Kata z komentarzem
źródło
Uważam, że ważne jest posiadanie testów obu typów i stosowanie ich w stosownych przypadkach.
Jak powiedziałeś, istnieją dwie skrajności i szczerze mówiąc, nie zgadzam się z żadną z nich.
Kluczem jest to, że testy jednostkowe muszą obejmować zasady i wymagania biznesowe . Jeśli istnieje wymóg, że system musi śledzić wiek osoby, napisz „trywialne” testy, aby upewnić się, że wiek jest nieujemną liczbą całkowitą. Testujesz domenę danych wymaganych przez system: choć trywialny, ma wartość, ponieważ wymusza parametry systemu .
Podobnie w przypadku bardziej złożonych testów muszą one przynosić wartość. Jasne, możesz napisać test, który potwierdzi coś, co nie jest wymogiem, ale powinno być egzekwowane gdzieś w wieży z kości słoniowej, ale jest to czas, który lepiej spędzić na pisaniu testów, które potwierdzają wymagania, za które klient ci płaci. Na przykład, dlaczego napisać test, który potwierdzi kod, może poradzić sobie ze strumieniem wejściowym, który przekroczy limit czasu, gdy jedyne strumienie pochodzą z plików lokalnych, a nie z sieci?
Mocno wierzę w testowanie jednostkowe i używam TDD tam, gdzie ma to sens. Testy jednostkowe z pewnością przynoszą wartość w postaci podwyższonej jakości i zachowania „szybkiego niepowodzenia” przy zmianie kodu. Należy jednak pamiętać o starej zasadzie 80/20 . W pewnym momencie osiągniesz malejące zwroty podczas pisania testów i musisz przejść do bardziej produktywnej pracy, nawet jeśli istnieje pewna wymierna wartość z pisania większej liczby testów.
źródło
Oto moje zdanie: wszystkie testy mają koszty:
Zamierzamy również, aby wszystkie testy zapewniały korzyści (i z mojego doświadczenia wynika, że prawie wszystkie testy zapewniają korzyści):
Łatwo więc zauważyć, że jeśli napiszesz wiele testów, prawdopodobnie będą one miały jakąś wartość. To się komplikuje, gdy zaczynasz porównywać tę wartość (której, nawiasem mówiąc, możesz nie wiedzieć z góry - jeśli wyrzucisz kod, testy regresji tracą swoją wartość) do kosztu.
Teraz twój czas i wysiłek są ograniczone. Chcesz robić rzeczy, które zapewniają największe korzyści przy jak najmniejszych kosztach. I myślę, że jest to bardzo trudna rzecz, zwłaszcza dlatego, że może wymagać wiedzy, której nie ma się lub byłoby drogie.
I to jest prawdziwy rozziew między tymi różnymi podejściami. Uważam, że wszyscy zidentyfikowali strategie testowania, które są korzystne. Jednak każda strategia generalnie ma inne koszty i korzyści. Ponadto koszty i korzyści każdej strategii będą prawdopodobnie w dużym stopniu zależne od specyfiki projektu, dziedziny i zespołu. Innymi słowy, może istnieć wiele najlepszych odpowiedzi.
W niektórych przypadkach wypompowywanie kodu bez testów może zapewnić najlepsze korzyści / koszty. W innych przypadkach dokładniejszy zestaw testów może być lepszy. W jeszcze innych przypadkach poprawa projektu może być najlepszym rozwiązaniem.
źródło
Co jest jednostka testy, naprawdę? I czy w grze jest tak duża dychotomia?
Pracujemy w dziedzinie, w której czytanie dosłownie jednego bitu poza końcem bufora może całkowicie spowodować awarię programu lub spowodować, że da on całkowicie niedokładny wynik, lub jak dowodzi niedawny błąd TLS „HeartBleed”, rozłożyć rzekomo bezpieczny system na cały system otwarte bez przedstawienia żadnych bezpośrednich dowodów wady.
Niemożliwe jest wyeliminowanie całej złożoności tych systemów. Ale naszym zadaniem jest, w możliwym zakresie, zminimalizowanie tej złożoności i zarządzanie nią.
Czy test jednostkowy jest testem, który potwierdza na przykład, że rezerwacja została pomyślnie wysłana w trzech różnych systemach, tworzony jest wpis do dziennika i wysyłane jest potwierdzenie e-mailem?
Powiem nie . To test integracyjny . A te zdecydowanie mają swoje miejsce, ale są też innym tematem.
Test integracji działa w celu potwierdzenia ogólnej funkcji całej „funkcji”. Ale kod stojący za tą funkcją powinien zostać podzielony na proste, sprawdzalne elementy składowe, zwane także „jednostkami”.
Test jednostkowy powinien więc mieć bardzo ograniczony zakres.
Co oznacza, że kod testowany przez test jednostkowy powinien mieć bardzo ograniczony zakres.
Co dodatkowo oznacza, że jednym z filarów dobrego projektu jest rozbicie złożonego problemu na mniejsze, jednozadaniowe części (w możliwym zakresie), które można przetestować we względnej izolacji od siebie.
W rezultacie otrzymujesz system wykonany z niezawodnych komponentów fundamentowych i wiesz, czy którakolwiek z podstawowych jednostek kodu się psuje, ponieważ napisałeś proste, małe testy o ograniczonym zakresie, aby dokładnie to powiedzieć.
W wielu przypadkach prawdopodobnie powinieneś mieć wiele testów na jednostkę. Same testy powinny być proste, testując jedno i tylko jedno zachowanie w możliwym zakresie.
Pojęcie „testu jednostkowego” testującego nietrywialną, skomplikowaną logikę jest, jak sądzę, trochę oksymoronem.
Jeśli więc nastąpił taki celowy podział projektu, to jak na świecie test jednostkowy mógłby nagle zacząć generować fałszywe alarmy, chyba że podstawowa funkcja testowanej jednostki kodu uległa zmianie? A jeśli tak się stanie, lepiej uwierzyć, że w grze występują pewne nieoczywiste efekty tętnienia. Twój zepsuty test, ten, który wydaje się dawać fałszywie dodatni, tak naprawdę ostrzega cię, że jakaś zmiana przerwała szerszy krąg zależności w bazie kodu i należy go zbadać i naprawić.
Niektóre z tych jednostek (wiele z nich) mogą wymagać przetestowania przy użyciu fałszywych obiektów, ale to nie znaczy, że musisz pisać bardziej złożone lub skomplikowane testy.
Wracając do mojego contrived przykładzie systemu rezerwacji, naprawdę nie można wyłączyć wysyłanie żądań do bazy danych rezerwacji na żywo lub usług osób trzecich (lub nawet „dev” instancji nim) przed każdym jednostka testowego kodu.
Używasz więc makiet, które przedstawiają tę samą umowę interfejsu. Testy mogą następnie zweryfikować zachowanie stosunkowo niewielkiego, deterministycznego fragmentu kodu. Zielony na całej planszy następnie mówi, że bloki, które składają się na twoją podstawę, nie są zepsute.
Ale logika samych testów jednostkowych pozostaje tak prosta, jak to możliwe.
źródło
To oczywiście tylko mój sprzeciw, ale spędziłem kilka ostatnich miesięcy na nauce programowania funkcjonalnego w fsharp (pochodzącym z języka C #), uświadomiłem sobie kilka rzeczy.
Jak stwierdził PO, istnieją zazwyczaj 2 rodzaje „testów jednostkowych”, które widzimy każdego dnia. Testy obejmujące metody wejścia i wyjścia metody, które są na ogół najcenniejsze, ale trudne do wykonania dla 80% systemu, w którym mniej chodzi o „algorytmy”, a więcej o „abstrakcje”.
Innym typem jest testowanie interaktywności abstrakcji, zwykle polegające na kpinie. Moim zdaniem testy te są przede wszystkim konieczne ze względu na projekt twojej aplikacji. Ommiting ich, a ty ryzykujesz dziwnymi błędami i kodem spagetti, ponieważ ludzie nie myślą właściwie o swoim projekcie, chyba że zostaną zmuszeni do wykonania testów w pierwszej kolejności (a nawet wtedy, zwykle to psują). Problemem jest nie tyle metodologia testowania, co podstawowa konstrukcja systemu. Większość systemów zbudowanych z językami imperatywnymi lub OO ma wrodzoną zależność od „skutków ubocznych”, czyli „Rób to, ale nic mi nie mów”. Kiedy polegasz na skutku ubocznym, musisz go przetestować, ponieważ wymaganie biznesowe lub operacja jest zwykle jego częścią.
Kiedy projektujesz swój system w bardziej funkcjonalny sposób, w którym unikasz budowania zależności od skutków ubocznych i unikasz zmian stanu / śledzenia poprzez niezmienność, pozwala ci to skoncentrować się bardziej na testach „ins in out”, które wyraźnie testują więcej akcji , a tym bardziej, jak się tam dostać. Zdziwisz się, co może dać niezmienność w zakresie znacznie prostszych rozwiązań tych samych problemów, a kiedy nie będziesz już zależny od „efektów ubocznych”, możesz robić rzeczy takie jak programowanie równoległe i asynchroniczne bez prawie dodatkowych kosztów.
Odkąd zacząłem pisać w Fsharp, do niczego nie potrzebowałem frameworku, a nawet całkowicie porzuciłem swoją zależność od kontenera IOC. Moje testy są oparte na potrzebach biznesowych i wartości, a nie na ciężkich warstwach abstrakcji zwykle potrzebnych do uzyskania kompozycji w programowaniu imperatywnym.
źródło