Jeśli kod testu jednostkowego „wącha”, czy to naprawdę ma znaczenie?

52

Zwykle po prostu zrzucam swoje testy jednostkowe za pomocą kopiowania i wklejania oraz wszelkiego rodzaju innych złych praktyk. Testy jednostkowe zwykle wyglądają dość brzydko, są pełne „zapachu kodu”, ale czy to naprawdę ma znaczenie? Zawsze powtarzam sobie, o ile „prawdziwy” kod jest „dobry”, to wszystko, co się liczy. Ponadto testy jednostkowe zwykle wymagają różnych „śmierdzących hacków”, takich jak funkcje kikutów.

W jaki sposób powinienem przejmować się źle zaprojektowanymi („śmierdzącymi”) testami jednostkowymi?

Przyciski 840
źródło
8
Jak trudne może być naprawienie kodu, który zawsze kopiujesz?
JeffO
7
zapach kodu w testach może łatwo wskazywać na ukryty zapach w pozostałej części kodu - po co podchodzić do testów tak, jakby nie były „prawdziwym” kodem?
HorusKol

Odpowiedzi:

75

Czy zapach jednostkowy jest ważny? Tak, zdecydowanie. Jednak są one odmienne od zapachy kodu ponieważ testy jednostkowe służą innym celom i mają inny zestaw napięć, które informują ich konstrukcji. Wiele zapachów w kodzie nie dotyczy testów. Biorąc pod uwagę moje TDD mentalność, chciałbym rzeczywiście twierdzą, że zapachy testów jednostkowych są bardziej istotne niż zapachy kodu, ponieważ kod jest właśnie tam, aby spełniać wymagania badań.

Oto kilka typowych zapachów testowania jednostkowego:

  • Kruchość : czy twoje testy często i nieoczekiwanie kończą się niepowodzeniem nawet w przypadku pozornie trywialnych lub niezwiązanych ze sobą zmian kodu?
  • Przeciek stanu : czy twoje testy kończą się inaczej w zależności od, na przykład, w jakiej kolejności są uruchamiane?
  • Setup / Teardown Bloat : Czy twoje bloki przygotowania / porzucenia są długie i rosną dłużej? Czy wykonują jakąkolwiek logikę biznesową?
  • Slow Runtime : Czy testy trwają długo? Czy któryś z indywidualnych testów jednostkowych trwa dłużej niż jedna dziesiąta sekundy? (Tak, mówię poważnie, jedna dziesiąta sekundy.)
  • Tarcie : Czy istniejące testy utrudniają pisanie nowych testów? Czy często zmagasz się z niepowodzeniem testu podczas refaktoryzacji?

Znaczenie zapachów polega na tym, że są one użytecznymi wskaźnikami projektowania lub innymi bardziej fundamentalnymi kwestiami, tj. „Tam, gdzie jest dym, tam jest ogień”. Nie szukaj tylko zapachów testowych, szukaj też ich przyczyny.

Z drugiej strony oto kilka dobrych praktyk dotyczących testów jednostkowych:

  • Szybka, skoncentrowana informacja zwrotna : Twoje testy powinny szybko izolować awarię i dostarczać użytecznych informacji o przyczynie.
  • Minimalizuj odległość kodu testowego : Powinna istnieć jasna i krótka ścieżka między testem a kodem, który go implementuje. Duże odległości tworzą niepotrzebnie długie pętle sprzężenia zwrotnego.
  • Testuj jedną rzecz naraz : Testy jednostkowe powinny testować tylko jedną rzecz. Jeśli chcesz przetestować inną rzecz, napisz kolejny test.
  • Błąd to test, o którym zapomniałeś napisać : Czego możesz się nauczyć z tego, że nie napisałeś lepszych, pełniejszych testów w przyszłości?
Rein Henrichs
źródło
2
„Testy jednostkowe służą innym celom i mają inny zestaw napięć, które wpływają na ich konstrukcję”. Na przykład powinny DAMP, niekoniecznie DRY.
Jörg W Mittag
3
@ Jörg zgodził się, ale czy DAMP faktycznie jest czymś? : D
Rein Henrichs
5
@ Rein: opisowe i znaczące zwroty. Również nie do końca SUCHO. Patrz codehelter.wordpress.com/2011/04/07/...
Robert Harvey
+1. Nie znałem też znaczenia DAMP.
egarcia
2
@ironcode Jeśli nie możesz pracować na jednym systemie w izolacji bez obawy, że przerwiesz jego integrację z innymi systemami, pachnie to dla mnie ścisłym sprzężeniem. Jest to właśnie cel identyfikacji zapachów testowych, takich jak długi czas działania: informują one o problemach projektowych, takich jak ścisłe połączenie. Odpowiedź nie powinna brzmieć „Och, więc ten testowy zapach jest nieprawidłowy”, powinna brzmieć „Co ten zapach mówi mi o moim projekcie?” Testy jednostkowe określające zewnętrzny interfejs systemu powinny wystarczyć, aby stwierdzić, czy zmiany przerywają integrację z klientami.
Rein Henrichs
67

Obawiać się. Piszecie testy jednostkowe, aby udowodnić, że kod działa zgodnie z oczekiwaniami. Pozwalają szybko i pewnie refaktoryzować. Jeśli twoje testy są delikatne, trudne do zrozumienia lub trudne do utrzymania, zignorujesz testy zakończone niepowodzeniem lub je wyłączysz w miarę ewoluowania bazy kodu, negując w pierwszej kolejności wiele zalet pisania testów.

jayraynet
źródło
17
+1 W momencie, gdy zaczniesz ignorować niepomyślne testy, nie dodają żadnej wartości.
19

Właśnie skończyłem czytać The Art of Unit Testing kilka dni temu. Autor opowiada się za poświęceniem tyle samo uwagi testom jednostkowym, co kodu produkcyjnego.

Doświadczyłem z jednej strony źle napisanych, niemożliwych do utrzymania testów. Napisałem własne. Jest praktycznie gwarantowane, że jeśli test jest trudny w utrzymaniu, nie zostanie przeprowadzony. Gdy testy nie są zsynchronizowane z testowanym kodem, stają się gniazdami kłamstw i podstępów. Istotą testów jednostkowych jest wzbudzenie zaufania, że ​​niczego nie zepsuliśmy (to znaczy, że budują zaufanie). Jeśli nie można ufać testom, są one gorsze niż bezużyteczne.

Joshua Smith
źródło
4
+1 musisz utrzymać testy, podobnie jak kod produkcyjny
Hamish Smith
Kolejną bardzo dobrą książką na ten temat jest „Wzorce testowe xUnit Gerarda Meszarosa - Kod testowy ponownego odniesienia”.
adrianboimvaser
7

Tak długo, jak test jednostkowy faktycznie testuje kod w „większości” przypadków. (Mówiono najbardziej celowo, ponieważ czasami trudno jest znaleźć wszystkie możliwe wyniki). Myślę, że „śmierdzący” kod jest twoją osobistą preferencją. Zawsze piszę kod w taki sposób, aby go odczytać i zrozumieć w ciągu kilku sekund, zamiast przekopywać śmieci i próbować zrozumieć, co jest. Zwłaszcza, gdy wrócisz do niego po znacznym czasie.

Podsumowując - jego testowanie powinno być łatwe. Nie chcesz mylić się z „śmierdzącym” kodem.

Łukasz
źródło
5
+1: Nie przejmuj się kodem testu jednostkowego. Niektóre z najgłupszych pytań dotyczą uczynienia kodu testu jednostkowego bardziej „solidnym”, aby zmiana programowa nie przerwała testu jednostkowego. Głupota. Testy jednostkowe są zaprojektowane tak, aby były kruche. Jest ok, jeśli są mniej niezawodne niż kod aplikacji, który zostały zaprojektowane do testowania.
S.Lott,
1
@ S.Lott Zarówno zgadzam się, jak i nie zgadzam, z powodów lepiej wyjaśnionych w mojej odpowiedzi.
Rein Henrichs
1
@ Rein Henrichs: są to o wiele poważniejsze zapachy niż opisano w pytaniu. „kopiuj i wklej” i „wyglądają dość brzydko” nie brzmią tak źle, jak zapachy, które opisujesz, gdy testy są niewiarygodne.
S.Lott,
1
@ S. Dokładnie, myślę, że jeśli będziemy rozmawiać o „zapachach testowania jednostkowego”, należy wprowadzić takie rozróżnienie. Zapachy kodu w testach jednostkowych często nie są. Są napisane do różnych celów, a napięcia, które sprawiają, że śmierdzą w jednym kontekście, są bardzo różne w drugim.
Rein Henrichs
2
@ S.Lott btw wydaje się to idealną okazją do skorzystania z bardzo zaniedbanej funkcji czatu :)
Rein Henrichs
6

Zdecydowanie. Niektórzy twierdzą, że „każdy test jest lepszy niż brak testu”. Zdecydowanie się nie zgadzam - źle napisane testy skracają czas projektowania, a ty tracisz dni na naprawianie „zepsutych” testów, ponieważ nie były to dobre testy jednostkowe. W tej chwili dwie rzeczy, na których skupiam się, aby moje testy były cenne, a nie obciążenie, to:

Konserwowalność

Powinieneś testować wynik ( co się dzieje), a nie metodę ( jak to się dzieje). Twoja konfiguracja do testu powinna być możliwie jak najbardziej oddzielona od implementacji: ustaw wyniki tylko dla zgłoszeń serwisowych itp., Które są absolutnie konieczne.

  • Użyj kpiny, aby upewnić się, że twoje testy nie zależą od niczego zewnętrznego
  • Jeśli to możliwe, faworyzuj kody pośredniczące nad próbnymi (jeśli Twoja struktura je rozróżnia)
  • Brak logiki w testach! Ifs, przełączniki, for-eaches, sprawy, try-catch itp. Są dużymi „nie-nosami”, ponieważ mogą wprowadzać błędy w samym kodzie testowym

Czytelność

Można pozwolić na nieco więcej powtórzeń w testach, co normalnie nie byłoby dozwolone w kodzie produkcyjnym, jeśli dzięki temu są bardziej czytelne. Po prostu zrównoważy to powyższymi zagadnieniami dotyczącymi konserwacji. Wyraźnie określ, co robi test!

  • Staraj się zachować styl „aranżuj, działaj, potwierdzaj” dla swoich testów. To oddziela twoją konfigurację i oczekiwania od scenariusza, od wykonywanej akcji i potwierdzanego wyniku.
  • Zachowaj jedno logiczne potwierdzenie na test (jeśli nazwa testu zawiera „i”, może być konieczne rozbicie go na wiele testów)

Podsumowując, powinieneś bardzo martwić się „śmierdzącymi” testami - mogą skończyć się stratą czasu, nie zapewniając żadnej wartości.

Powiedziałeś:

Testowanie jednostkowe zwykle wymaga różnych „śmierdzących hacków”, takich jak funkcje stubowania.

Wygląda na to, że na pewno możesz zrobić to, czytając niektóre techniki Testowania Jednostkowego, takie jak korzystanie z frameworka Mocking, aby Twoje życie było o wiele łatwiejsze. Bardzo bardzo polecam Art of Unit Testing , który obejmuje powyższe i wiele więcej. Uznałem to za pouczające po długim zmaganiu się ze źle napisanymi, niemożliwymi do utrzymania, „śmierdzącymi” testami. To jedna z najlepszych inwestycji, jakie poczyniłem w tym roku!

mjhilton
źródło
Doskonała odpowiedź. Mam tylko jedną małą sprzeczkę: znacznie ważniejsza niż „jedno logiczne potwierdzenie na test” to jedno działanie na test. Chodzi o to, bez względu na to, ile kroków trzeba wykonać, aby zorganizować test, powinna istnieć tylko jedna „testowana akcja kodu produkcyjnego”. Jeśli wspomniane działanie powinno mieć więcej niż jeden efekt uboczny, możesz mieć wiele twierdzeń.
Rozczarowany
5

Dwa pytania do ciebie:

  • Czy jesteś absolutnie pewien, że testujesz to, co według Ciebie testujesz?
  • Jeśli ktoś spojrzy na test jednostkowy, czy będzie w stanie dowiedzieć się, co powinien zrobić kod ?

Istnieją sposoby radzenia sobie z powtarzającymi się zadaniami w testach jednostkowych, które najczęściej są konfigurowane i usuwane. Zasadniczo masz metodę konfiguracji testu i testową metodę odrywania - wszystkie ramy testów jednostkowych obsługują to.

Testy jednostkowe powinny być małe i łatwe do zrozumienia. Jeśli nie są, a test się nie powiedzie, jak zamierzasz rozwiązać problem w stosunkowo krótkim czasie. Ułatw sobie życie przez kilka miesięcy, kiedy będziesz musiał wrócić do kodu.

Berin Loritsch
źródło
5

Kiedy kosz zaczyna pachnieć, czas go wynieść. Twój kod testowy powinien być tak czysty, jak kod produkcyjny. Czy pokazałbyś to swojej matce?

Bill Leeper
źródło
3

Oprócz innych odpowiedzi tutaj.

Kod niskiej jakości w testach jednostkowych nie jest ograniczony do zestawu testów jednostkowych.

Jedną z ról pełnionych przez testowanie jednostkowe jest dokumentacja.

Pakiet testów jednostkowych jest jednym z miejsc, w których szuka się informacji o tym, w jaki sposób ma być używany interfejs API.

Osoby wywołujące interfejs API nie są w stanie skopiować części pakietu testów jednostkowych, co prowadzi do tego, że zły kod pakietu testowego infekuje kod aktywny w innym miejscu.

Paul Butcher
źródło
3

Prawie nie podałem odpowiedzi, ponieważ zajęło mi chwilę zastanowienie się, jak podejść do tego jako uzasadnionego pytania.

Powody, dla których programiści angażują się w „najlepsze praktyki”, nie są ze względów estetycznych lub dlatego, że chcą „idealnego” kodu. To dlatego, że oszczędza im czas.

  • Oszczędza czas, ponieważ dobry kod jest łatwiejszy do odczytania i zrozumienia.
  • Oszczędza czas, ponieważ gdy trzeba znaleźć błąd, łatwiej i szybciej go zlokalizować.
  • Oszczędza to czas, ponieważ kiedy chcesz rozszerzyć kod, jest to łatwiejsze i szybsze.

Pytanie, które zadajesz (z mojego punktu widzenia), to czy powinienem oszczędzić sobie czas, czy napisać kod, który zmarnuje mój czas?

Na to pytanie mogę tylko powiedzieć, jak ważny jest twój czas?

Dla waszej informacji: karczowanie, kpiny i łatanie małp mają wszystkie uzasadnione zastosowania. Pachną tylko wtedy, gdy ich użycie jest niewłaściwe.

dietbuddha
źródło
2

Staram się NIE sprawiać, by moje testy jednostkowe były zbyt solidne. Widziałem testy jednostkowe, które zaczynają robić obsługę błędów w imię solidności. To, co kończysz, to testy, które pochłaniają błędy, które próbują złapać. Testy jednostkowe również często muszą robić fajne rzeczy, aby wszystko działało. Pomyśl o całym pomyśle prywatnych akcesorów korzystających z refleksji ... gdybym zobaczył kilka z nich w kodzie produkcyjnym, byłbym zmartwiony 9 razy na 10. Myślę, że należy poświęcić więcej czasu na zastanowienie się nad tym, co jest testowane , a nie czystość kodu. Testy i tak będą musiały być dość często zmieniane, jeśli przeprowadzasz jakieś poważne refaktoryzacje, więc dlaczego nie po prostu zhakować je razem, mieć trochę mniej poczucia własności i mieć większą motywację do przepisywania lub przerabiania ich, gdy nadejdzie czas?

Morgan Herlocker
źródło
2

Jeśli wybierasz się na intensywny, niskopoziomowy zasięg, będziesz spędzać tyle samo czasu lub więcej w kodzie testowym, jak w kodzie produktu, wykonując poważne modyfikacje.

W zależności od złożoności konfiguracji testu kod może być bardziej złożony. (httpcontext.current vs. straszny potwór próbujący na przykład zbudować fałszywego)

Jeśli twój produkt nie jest czymś, w którym rzadko dokonujesz przełomowej zmiany w istniejących interfejsach, a dane wejściowe na poziomie jednostki są bardzo proste w konfiguracji, byłbym NAJMNIEJ tak samo zmartwiony zrozumieniem testów jak rzeczywisty produkt.

Rachunek
źródło
0

Zapachy kodu to tylko problem w kodzie, który w pewnym momencie wymaga zmiany lub zrozumienia.

Myślę więc, że powinieneś naprawić zapachy kodu, kiedy musisz zbliżyć się do danego testu jednostkowego, ale nie wcześniej.

Ian
źródło