Czy zbyt wiele stwierdzeń ma zapach?

19

Naprawdę zakochałem się w testach jednostkowych i TDD - jestem zainfekowany testowo.

Jednak testy jednostkowe są zwykle stosowane w metodach publicznych. Czasami jednak muszę przetestować pewne założenia-twierdzenia również metodami prywatnymi, ponieważ niektóre z nich są „niebezpieczne”, a refaktoryzacja nie może dalej pomóc. (Wiem, ramy testowania umożliwiają testowanie metod prywatnych).

Stało się więc moim zwyczajem, że zarówno pierwsza, jak i ostatnia linia metody prywatnej są twierdzeniami.

Zauważyłem jednak, że zwykle używam twierdzeń w metodach publicznych (a także prywatnych) tylko dla pewności. Czy może to być „testowanie duplikacji”, ponieważ założenia metody publicznej są testowane z zewnątrz przez system testów jednostkowych?

Czy ktoś mógłby pomyśleć o zbyt wielu twierdzeniach jako zapachu kodu?

Florents Tselai
źródło
1
czy te twierdzenia są włączone czy wyłączone w wydaniu?
jk.
Pracuję przede wszystkim z Javą, w której asercje muszą być włączane ręcznie.
Florents Tselai
Właśnie znalazłem to „Zapewnia wzrost wydajności poprzez śledzenie błędów” programmers.stackexchange.com/a/16317/32342
Florents Tselai
jak definiujesz „za dużo”?
haylem
@haylem Istnieje „zbyt wiele” stwierdzeń, jeśli inny programista odczytuje kod i mówi „Mam dość tych stwierdzeń”.
Florents Tselai

Odpowiedzi:

26

Te twierdzenia są naprawdę przydatne do testowania twoich założeń, ale służą także innemu bardzo ważnemu celowi: dokumentacji. Każdy czytelnik metody publicznej może odczytać stwierdzenia, aby szybko określić warunki wstępne i końcowe, bez konieczności patrzenia na zestaw testów. Z tego powodu zalecam trzymanie tych asertów ze względów związanych z dokumentacją, a nie ze względów testowych. Technicznie powielasz twierdzenia, ale służą one dwóm różnym celom i są bardzo przydatne w obu przypadkach.

Zachowanie ich jako zapewnień jest lepsze niż zwykłe stosowanie komentarzy, ponieważ aktywnie sprawdzają założenia przy każdym uruchomieniu.

Oleksi
źródło
2
Bardzo dobra uwaga!
Florents Tselai
1
Asercje to dokumentacja, ale nie uzasadnia to powielania ich. Przeciwnie, budzi to nawet podejrzenia co do jakości testów, jeśli wydaje się, że te same twierdzenia są potrzebne więcej niż raz.
Yam Marcovic,
1
@YamMarcovic Myślę, że jest to dopuszczalne, ponieważ dwa „zduplikowane” fragmenty kodu służą dwóm różnym i odrębnym celom. Myślę, że warto je mieć w obu lokalizacjach, pomimo duplikacji. Posiadanie twierdzeń w metodzie jest nieocenione w dokumentacji i są one oczywiście wymagane do testowania.
Oleksi
2
Asercje w kodzie są dla mnie komplementarne do asercji testowania jednostkowego. Nie będziesz mieć stuprocentowego pokrycia testowego, a stwierdzenia w kodzie pomogą ci zawęzić zawężające testy jednostkowe szybciej.
sebastiangeiger
15

Wygląda na to, że próbujesz wykonać projekt według umowy ręcznie.

Wykonanie DbC jest dobrym pomysłem, ale powinieneś przynajmniej rozważyć przejście na język, który obsługuje go natywnie (taki jak Eiffel ) lub przynajmniej użyć ramowej umowy dla swojej platformy (np. Microsoft Code Contracts dla .NETjest całkiem niezły, prawdopodobnie najbardziej wyrafinowany system kontraktów, nawet potężniejszy niż Eiffel). W ten sposób możesz lepiej wykorzystać siłę kontraktów. Np. Używając istniejącego frameworka, możesz ujawnić kontrakty w dokumentacji lub IDE (np. Kontrakty kodowe dla .NET są pokazane w IntelliSense, a jeśli masz VS Ultimate, możesz nawet sprawdzić statycznie przez automatyczną weryfikację twierdzeń w czasie kompilacji, podczas gdy piszesz; podobnie wiele ram kontraktów Java ma dokumentację JavaDoc, która automatycznie wyodrębni kontrakty do dokumentacji interfejsu API JavaDoc).

I nawet jeśli okaże się, że w twojej sytuacji nie ma alternatywy dla zrobienia tego ręcznie, teraz przynajmniej wiesz, jak się nazywa, i możesz o tym poczytać.

Krótko mówiąc: jeśli rzeczywiście robisz DbC, nawet jeśli o tym nie wiesz, te twierdzenia są całkowicie w porządku.

Jörg W Mittag
źródło
4

Ilekroć nie masz pełnej kontroli nad parametrami wejściowymi, bardzo dobrym pomysłem jest przetestowanie z góry prostych błędów. Na przykład brak wartości null.

To nie jest powielanie swoich badań, jak powinny sprawdzić, czy kod zawiedzie odpowiednio podane złych parametrów wejściowych, a następnie udokumentować , że .

Nie sądzę, że powinieneś twierdzić o parametrach zwracanych (chyba że masz jawnie niezmiennik, który czytelnik powinien zrozumieć). Jest to także praca najbardziej nieprzystosowanych.

Osobiście nie podoba mi się to assertw Javie, ponieważ można je wyłączyć, a wtedy jest to fałszywe zabezpieczenie.


źródło
1
+1 za „Nie podoba mi się instrukcja aserta w Javie, ponieważ można ją wyłączyć, a wtedy jest to fałszywe zabezpieczenie”.
Florents Tselai
3

Myślę, że stosowanie asercji w metodach publicznych jest jeszcze ważniejsze, ponieważ tam nie kontrolujesz danych wejściowych i istnieje większe prawdopodobieństwo złamania założenia.

Testowanie warunków wejściowych powinno odbywać się we wszystkich metodach publicznych i chronionych. Jeśli dane wejściowe są przekazywane bezpośrednio do metody prywatnej, testowanie danych wejściowych może być zbędne.

Testowanie warunków wyjściowych (np. Stan obiektu lub zwracana wartość! = Null) powinno odbywać się w metodach wewnętrznych (np. Metodach prywatnych). Jeśli dane wyjściowe są przekazywane bezpośrednio z metody prywatnej do wyniku metody publicznej bez dodatkowych obliczeń lub zmian stanu wewnętrznego, wówczas testowanie warunków wyjściowych metody publicznej może być zbędne.

Zgadzam się jednak z Oleksi, że redundancja może zwiększyć czytelność, a także może zwiększyć konserwowalność (jeśli bezpośrednie przydzielanie lub zwrot nie będzie już w przyszłości).

Danny Varod
źródło
4
Jeśli warunek błędu lub niepoprawny warunek argumentu może być spowodowany przez użytkownika (wywołującego) interfejsu publicznego, należy temu zapewnić pełne odpowiednie postępowanie z błędami. Oznacza to sformatowanie komunikatu o błędzie, który jest znaczący dla użytkownika końcowego, wraz z kodem błędu, rejestrowaniem i próbami odzyskania danych lub przywrócenia do normalnego stanu. Zastąpienie prawidłowej obsługi błędów twierdzeniami sprawi, że kod będzie mniej niezawodny i trudniejszy do rozwiązania.
rwong
Twierdzenia mogą (i w wielu przypadkach powinny) obejmować rejestrowanie i zgłaszanie wyjątków (lub kodów błędów w C--).
Danny Varod,
3

Trudno jest być niezależnym od języka w tej kwestii, ponieważ szczegóły, w jaki sposób realizowane są twierdzenia i „poprawna” obsługa błędów / wyjątków, mają wpływ na odpowiedź. Oto moje 0,02 USD, oparte na mojej wiedzy o Javie i C ++.

Asercje w prywatnych metodach są dobre, zakładając, że nie przesadzasz i nie umieszczasz ich wszędzie . Jeśli stosujesz naprawdę proste metody lub wielokrotnie sprawdzasz rzeczy takie jak niezmienne pola, niepotrzebnie zaśmiecasz kod.

Twierdzeń w metodach publicznych na ogół najlepiej unikać. Nadal powinieneś robić takie rzeczy, jak sprawdzanie, czy umowa metody nie jest naruszona, ale jeśli tak, powinieneś zgłaszać wyjątki o odpowiednim typie z sensownymi komunikatami, w miarę możliwości przywracać stan itp. (Co @rwong nazywa „pełnym” właściwe postępowanie z błędami ”).

Ogólnie rzecz biorąc, należy używać tylko twierdzeń, aby pomóc swój rozwój / debugowania. Nie możesz założyć, że ktokolwiek używa twojego API, będzie miał nawet włączone asercje, gdy użyje twojego kodu. Chociaż mają one pewne zastosowanie w dokumentowaniu kodu, często istnieją lepsze sposoby dokumentowania tych samych rzeczy (tj. Dokumentacji metod, wyjątków).

Vaughandroid
źródło
2

Dodając do listy (głównie) wyjątkowych odpowiedzi, innym powodem wielu twierdzeń i powielania, jest to, że nie wiesz jak, kiedy lub przez kogo klasa zostanie zmodyfikowana w ciągu jej życia. Jeśli piszesz wyrzuć kod, który będzie martwy za rok, nie martw się. Jeśli piszesz kod, który będzie używany za ponad 20 lat, będzie wyglądał zupełnie inaczej - a przyjęte przez Ciebie założenie może już nie być aktualne. Facet, który dokona tej zmiany, podziękuje ci za twierdzenia.

Również nie wszyscy programiści są doskonali - ponownie twierdzenia oznaczają, że jeden z tych „wpadek” nie rozprzestrzeni się zbyt daleko.

Nie lekceważ wpływu, jaki te twierdzenia (i inne kontrole założeń) będą miały na obniżenie kosztów utrzymania.

mattnz
źródło
0

Posiadanie duplikatów stwierdzeń nie jest samo w sobie złe, ale posiadanie takich stwierdzeń „po prostu upewnienie się” nie jest najlepsze. To naprawdę sprowadza się do tego, co dokładnie każdy test próbuje osiągnąć. Podaj tylko to, co jest absolutnie potrzebne. Jeśli test używa Moq do sprawdzenia, czy metoda została wywołana, tak naprawdę nie ma znaczenia, co stanie się po wywołaniu tej metody, test dotyczy jedynie upewnienia się, że metoda została wywołana.

Jeśli każdy pojedynczy test jednostkowy zawiera ten sam zestaw twierdzeń, z wyjątkiem jednego lub dwóch, wówczas po nieco refaktoryzacji wszystkie testy mogą zakończyć się niepowodzeniem z tego samego powodu, zamiast pokazywać prawdziwy wpływ refaktora. Możesz skończyć w sytuacji, gdy uruchomisz wszystkie testy, wszystkie kończą się niepowodzeniem, ponieważ każdy test ma te same stwierdzenia, naprawiasz tę awarię, uruchamiasz testy ponownie, nieudane z innego powodu, naprawiasz tę awarię. Powtarzaj, aż skończysz.

Weź również pod uwagę utrzymanie projektu testowego. Co się stanie, gdy dodasz nowy parametr lub dane wyjściowe zostaną nieco zmodyfikowane, czy musiałbyś wrócić przez szereg testów i zmienić aser lub dodać aser? I w zależności od kodu możesz skończyć o wiele więcej, aby upewnić się, że wszystkie twierdzenia przejdą.

Rozumiem urok zachęcania do uwzględnienia duplikatów w teście jednostkowym, ale to naprawdę przesada. Poszedłem tą samą ścieżką z moim pierwszym projektem testowym. Odszedłem od tego, ponieważ sama konserwacja doprowadzała mnie do szału.

bwalk2895
źródło
0

Testy jednostkowe są jednak stosowane w metodach publicznych.

Jeśli Twoja struktura testowania pozwala na akcesoria, dobrym pomysłem jest również testowanie jednostkowe metod prywatnych (IMO).

Czy ktoś mógłby pomyśleć o zbyt wielu twierdzeniach jako zapachu kodu?

Ja robię. Twierdzenia są w porządku, ale twoim pierwszym celem powinno być zrobienie tego, aby scenariusz nie był nawet możliwy ; nie tylko, że tak się nie dzieje.

Telastyn
źródło
-2

To zła praktyka.

Nie powinieneś testować metod publicznych / prywatnych. Powinieneś przetestować całą klasę. Tylko upewnij się, że masz dobry zasięg.

Jeśli wybierzesz (prymitywny) sposób testowania każdej metody osobno jako ogólną zasadę, bardzo trudno będzie ci w przyszłości zmienić klasę. I to jedna z najlepszych rzeczy, które umożliwia TDD.

Poza tym jest to powielanie. Ponadto sugeruje, że autor kodu tak naprawdę nie wiedział, co robi. Zabawne jest to, że tak .

Niektóre osoby traktują swoje testy z mniejszą ostrożnością niż kod produkcyjny. Nie powinni. Jeśli już, to moje doświadczenie sugeruje nawet ostrożniejsze traktowanie testów. Są tego warte.

Yam Marcovic
źródło
-1 Jednostka testująca klasę jako całość może być niebezpieczna, ponieważ kończy się testowaniem więcej niż jednej rzeczy na test. To sprawia, że ​​testy są bardzo kruche. Powinieneś testować jedno zachowanie na raz, co często odpowiada testowaniu tylko jednej metody na test.
Oleksi
Również w odniesieniu do „sugeruje, że autor kodu tak naprawdę nie wiedział, co robi [...] ty.”. Przyszli programiści mogą nie mieć jasności co do tego, co robi konkretna metoda. W takim przypadku posiadanie warunków wstępnych i końcowych w postaci oświadczeń może być nieocenione w zrozumieniu fragmentu kodu.
Oleksi
2
@Oleksi Dobre testowanie dotyczy prawidłowych scenariuszy użytkowania, a nie zapewnienia każdego najmniejszego nieistotnego szczegółu. Nadmiarowe twierdzenia są rzeczywiście zapachem kodu, ponieważ niejasne zamiary, i są po prostu kolejnym złym przykładem programowania obronnego.
Yam Marcovic