Jakie są wady programowania pierwszego testu?

47

W dzisiejszych czasach jest cały wściekłość. „Wszyscy” to polecają. To samo w sobie sprawia, że ​​jestem podejrzliwy.

Jakie wady znalazłeś podczas tworzenia aplikacji testowych? Szukam osobistych doświadczeń od doświadczonych praktyków - potrafię czytać hipotetyczne przemyślenia stu niedoszłych osób w Internecie.

Pytam nie dlatego, że nienawidzę TDD, ale ponieważ moim zadaniem jest ulepszanie procesu tworzenia oprogramowania, a im więcej możemy dowiedzieć się o problemach, które napotykają ludzie, tym większa szansa na poprawę tego procesu.

Alex Feinman
źródło

Odpowiedzi:

41

Jest ich sporo, ale zalety zdecydowanie przewyższają wady.

Istnieje stroma krzywa uczenia się.

Wydaje się, że wielu programistów spodziewa się, że od pierwszego dnia będą mogli efektywnie programować od pierwszego testu. Niestety zdobycie doświadczenia i programowanie z taką samą prędkością jak wcześniej zajmuje dużo czasu. Nie da się tego obejść.

Mówiąc ściślej, bardzo łatwo się pomylić. Możesz bardzo łatwo (z bardzo dobrymi intencjami) napisać całą masę testów, które albo są trudne do utrzymania, albo testować niewłaściwe rzeczy. Trudno tu podać przykłady - tego rodzaju problemy wymagają doświadczenia. Musisz dobrze rozdzielić problemy i zaprojektować testowalność. Moja najlepsza rada tutaj to programowanie parami z kimś, kto naprawdę dobrze zna TDD.

Robisz więcej kodowania z góry.

Test-first oznacza, że ​​nie możesz pominąć testów (co jest dobre) i oznacza, że ​​skończysz pisać więcej kodu z góry. To oznacza więcej czasu. Znów nie da się tego obejść. Otrzymujesz nagrodę za kod, który jest łatwiejszy w utrzymaniu, rozszerzaniu i generalnie mniej błędów, ale zajmuje to trochę czasu.

Może być trudną sprzedażą dla menedżerów.

Menedżerowie oprogramowania zazwyczaj zajmują się tylko osiami czasu. Jeśli przejdziesz do programowania pierwszego testu i nagle poświęcasz 2 tygodnie na ukończenie funkcji zamiast jednej, nie spodoba im się to. To zdecydowanie bitwa, którą warto stoczyć, a wielu menedżerów jest wystarczająco oświeconych, aby ją zdobyć, ale może to być trudna sprzedaż.

Może być trudną sprzedażą dla innych programistów.

Ponieważ istnieje stroma krzywa uczenia się, nie wszyscy programiści lubią programować w pierwszej kolejności. W rzeczywistości zgaduję, że na początku większość programistów tego nie lubi. Możesz robić rzeczy takie jak programowanie parami, aby pomóc im nabierać prędkości, ale może to być trudna sprzedaż.

W końcu zalety przeważają nad wadami, ale nie pomaga, jeśli po prostu zignorujesz wady. Wiedza o tym, z czym masz do czynienia od samego początku, pomaga ci pokonać niektóre, jeśli nie wszystkie, wady.

Jaco Pretorius
źródło
To są dobre odpowiedzi, ale mogą być bardziej szczegółowe na temat nr 1? Szczególnie interesuje mnie informacja, w jaki sposób / czy udało ci się odzyskać szybkość programowania - czego nauczyłeś się, że nie wiedziałeś, kiedy zacząłeś robić TDD?
Alex Feinman,
Zaktualizowano w celu wyjaśnienia
Jaco Pretorius
7
Jeśli teraz przeprowadzasz testy, całkowity czas poświęcony na programowanie nie powinien się znacząco zmienić. Wygląda na to, że wszystko trwa dłużej, ponieważ poświęcasz czas na napisanie i utrzymanie testów jednostkowych.
ChrisF
1
@JeffO, czy znasz „Mam zamiar napisać sobie minivana!” szkoła kodowania?
Alex Feinman,
1
@tvanfosson - ponieważ próbują zmienić dwie rzeczy naraz - zarówno rozpoczęcie testów, jak i TDD - co może być problematyczne. Dodaje to także do szacunków czasu - całkiem poprawnie - więc menedżerowie i klienci widzą tylko wzrost z góry, a nie fakt, że całkowity czas jest faktycznie znany (choć raz), a nawet może być krótszy. Jeśli przeprowadzają jakieś testy, wzrost ten będzie mniejszy.
ChrisF
35

Test-first zakłada, że ​​piszesz kod, który jest:

  • testowalny w teście jednostkowym
  • to, co opracowujesz, ma oczywiste podejście i nie będzie wymagało obszernego prototypowania ani eksperymentowania
  • że nie trzeba zbyt mocno refaktoryzować lub że masz czas na wielokrotne przepisywanie setek lub tysięcy przypadków testowych
  • nic nie jest zapieczętowane
  • wszystko jest modułowe
  • wszystko można wstrzykiwać lub naśladować
  • że Twoja organizacja przykłada wystarczająco dużą wagę do niewielkich defektów, aby uzasadnić pochłanianie zasobów
  • że jest coś korzystnego do przetestowania na poziomie testu jednostkowego

Jeśli twój projekt nie spełnia tych wymagań, będziesz miał trudności. Organizatorzy TDD nie mają dobrych odpowiedzi na to pytanie, sugerując, że przeprojektowałeś swój produkt, aby lepiej mieścił się w tych liniach. Są sytuacje, w których jest to niemożliwe lub niepożądane.

W praktyce może też istnieć ogromny problem z ludźmi myślącymi, że testy w pierwszej kolejności faktycznie dowodzą cokolwiek na temat poprawnego działania programu. W wielu przypadkach nie jest to prawdą, ale nawet w przypadkach, w których jest to prawdą, daleki jest od pełnego obrazu poprawności. Ludzie widzą setki pozytywnych testów i zakładają, że bezpieczniej jest testować mniej, ponieważ przed TDD i tak zrobili tylko kilkaset przypadków testowych. Z mojego doświadczenia wynika, że ​​TDD oznacza, że ​​musisz mieć jeszcze więcej testów integracyjnych, ponieważ programiści będą mieli fałszywe zabezpieczenia, a ból związany ze zmianą wszystkich testów w celu przeprowadzenia dużego reduktora może skłonić programistów do ciekawych obejść.

Przykłady:

Mój osobisty najlepszy przykład to pisanie kodu bezpieczeństwa dla asp.net. Jeśli mają działać w nieprzyjaznym środowisku z konfiguracji maszyny, są otrzymywane, podpisywane i zapieczętowane, a ponieważ działają przeciwko obiektom boga IIS, bardzo trudno jest z nich kpić poprawnie. Dodaj pewne ograniczenia dotyczące wydajności i wykorzystania pamięci, a bardzo szybko stracisz elastyczność korzystania z obiektów zastępczych w pozostałych obszarach.

Jakikolwiek mikrokontroler lub inny kod środowiska o niskim poziomie zasobów może nie być w stanie wykonać projektu w stylu OO, ponieważ abstrakcje nie są optymalizowane i masz niskie limity zasobów. To samo można powiedzieć o procedurach o wysokiej wydajności w wielu przypadkach.

Rachunek
źródło
Czy możesz podać jakieś kontrprzykłady? Kiedy nie miałbym pisać czegoś, co da się przetestować w sposób testowy? Dlaczego nie miałbym pisać kodu, który można wyśmiewać lub wstrzykiwać (inny niż starszy kod, który sam w sobie jest tematem)?
Alex Feinman,
edytowano, aby dodać sekcję przykładów
Bill
4
Zgoda. Wydaje się, że działanie TDD opiera się na zestawie założeń dotyczących maszyn, z którymi pracujesz; wydaje się, że nie dotyczy to około 50% moich projektów.
Paul Nathan
Całkowicie się zgadzam ... Świetna odpowiedź
Khelben
2
To jak wszystko w tej grze - odpowiednie w wielu sytuacjach, nieodpowiednie dla innych. Uważaj na wszystkich, którzy opowiadają się za Jedną Prawdziwą Ścieżką w dowolnym obszarze rozwoju oprogramowania.
Alan B,
25

Największą wadą, jaką widziałem, nie jest z samym TDD, ale z praktykami. Przyjmują podejście dogmatyczne i gorliwe, w którym wszystko musi zostać przetestowane . Czasami (to znaczy wiele razy) nie jest to konieczne. Może to również nie być praktyczne (np. Wprowadzenie organizacji do TDD).

Dobry inżynier znajduje kompromisy i stosuje właściwą równowagę pomiędzy tym, kiedy / gdzie / jak zastosować najpierw test. Ponadto, jeśli ciągle spędzasz dużo więcej czasu na rozwijaniu testów zamiast rzeczywistego kodu (2-3 razy lub więcej), masz kłopoty.

Innymi słowy, bądź pragmatyczny i rozsądny z TDD (lub cokolwiek w rozwoju oprogramowania w tym zakresie).

luis.espinal
źródło
Czy to może stąd „nowa” definicja „starszego kodu” Michaela Feathersa (tj. „Kod bez testów”)?
Phill W.,
Ta definicja nie działałaby dla mnie :) Dla mnie każdy kod działający w środowisku produkcyjnym i podlegający zmianom jest kodem starszym, niezależnie od kodu lub jakości testu. Zazwyczaj kojarzymy „starszy kod” z „złym kodem” lub „przestarzałym kodem”, gdy w rzeczywistości zły kod i przestarzały kod są już obecne w opracowywanym kodzie, który nie był jeszcze wykorzystywany w produkcji. Naszym celem powinno być, aby nasz kod był od samego początku dziedzictwem, a jego jakość i użyteczność były wykorzystywane przez lata i dekady.
luis.espinal
6

Zacząłem robić TDD na początku sierpnia 2009 r. I przekonałem całą moją firmę do przejścia na nią we wrześniu / październiku 2009 r. Obecnie cały zespół deweloperów jest w pełni przekształcony, a przekazywanie niepoddanego testowi kodu do repo jest uważane za złą rzecz i narzucane. Działa to dla nas świetnie i nie wyobrażam sobie powrotu do kodowania dla kowbojów.

Istnieją jednak dwa problemy, które są dość zauważalne.

Zestaw testowy musi być utrzymany

Kiedy poważnie myślisz o TDD, skończysz pisać wiele testów. Ponadto, zajmuje trochę czasu i doświadczenia, aby uświadomić sobie, co jest właściwą szczegółowości testów (przesadza jest prawie tak źle, jak underdoing go). Testy te są także kodem i są podatne na bitrot. Oznacza to, że musisz zachować je tak, jak wszystko inne: zaktualizuj je, gdy aktualizujesz biblioteki, od których są zależne, od czasu do czasu zmieniaj parametry ... Gdy wprowadzisz duże zmiany w kodzie, wiele testów nagle się zdezaktualizuje lub nawet po prostu źle. Jeśli masz szczęście, możesz je po prostu usunąć, ale często zdarza się, że wyodrębniasz przydatne bity i dostosowujesz je do nowej architektury.

Od czasu do czasu testują abstrakcje

Używamy Django, który ma całkiem niezłą platformę testową. Czasami jednak przyjmuje założenia, które są nieco sprzeczne z rzeczywistością. Na przykład niektóre oprogramowanie pośrednie może przerwać testy. Lub niektóre testy przyjmują założenia dotyczące zaplecza buforowania. Ponadto, jeśli używasz „rzeczywistej” bazy danych (nie SQLite3), przygotowanie bazy danych do testów zajmie dużo czasu. Oczywiście możesz (i powinieneś) używać SQLite3 i bazy danych w pamięci do testów wykonywanych lokalnie, ale niektóre kody będą zachowywać się inaczej w zależności od używanej bazy danych. Konieczne jest skonfigurowanie serwera ciągłej integracji działającego w realistycznej konfiguracji.

(Niektórzy powiedzą ci, że powinieneś wyśmiewać wszystkie rzeczy, takie jak baza danych, w przeciwnym razie twoje testy nie będą „czyste”, ale to tylko ideologia. Jeśli popełnisz błędy w swoim kpiącym kodzie (i uwierz mi, to zrobisz), twoje testsuite będzie bezwartościowe).

To wszystko mówi, że problemy, które opisałem, zaczynają być zauważalne dopiero, gdy jesteś dość zaawansowany w TDD ... Kiedy dopiero zaczynasz w TDD (lub pracujesz przy mniejszych projektach), refaktoryzacja testów nie będzie problemem.

Ryszard Szopa
źródło
3
+1. „musi być utrzymany”: jest to o wiele mniejszy problem podczas testowania kodu wielokrotnego użytku, ponieważ jego interfejs i zachowanie zwykle muszą być stabilne. Z tego powodu zwykle robię TDD tylko dla naszej biblioteki wielokrotnego użytku.
Dimitri C.
4

Dla mnie jest jakiś głęboki problem psychologiczny z testami, ilekroć próbuję je szeroko zastosować, jak w TDD: jeśli tam są, koduję niechlujnie, ponieważ ufam, że testy wychwycą jakiekolwiek problemy. Ale jeśli nie ma testów zapewniających siatkę bezpieczeństwa, koduję ostrożnie, a wynik jest niezmiennie lepszy niż w przypadku testów.

Może to tylko ja. Ale czytałem też gdzieś, że samochody z wszelkiego rodzaju dzwonkami i gwizdkami bezpieczeństwa częściej się rozbijają (ponieważ kierowcy wiedzą, że są tam zabezpieczenia), więc może warto to uznać; TDD może być niekompatybilny z niektórymi osobami.

Joonas Pulakka
źródło
Wydaje mi się to dziwne, ponieważ pisanie testowalnego kodu zwykle powoduje, że zwalniam i zastanawiam się więcej nad tym, co koduję. W dzisiejszych czasach mam trochę nerwowe kodowanie bez testów.
Matt H
1
To po prostu pokazuje, że różni ludzie rzeczywiście reagują inaczej. Nie walę w TDD - oczywiście sporo osób uważa to za przydatne - ale faktem jest, że nie dla wszystkich.
Joonas Pulakka
2
Zgadzam się w 100%. Piszę kod lepiej i szybciej bez automatycznych testów. Oczywiście absurdem byłoby nie testować, myślę, że automatyzacja to zły wybór (przynajmniej dla mnie). Uważam, że ręczne testowanie jest zarówno szybsze niż utrzymywanie pakietu testowego, jak i bezpieczniejsze - ale jestem również doświadczonym programistą, więc bardzo dobrze wiem, co testować, gdzie i dlaczego, więc moje dodatki do kodu i zmiany czynników są bez regresji.
Ben Lee,
1
Chociaż powinienem zauważyć, że zespół, z którym pracuję, i oba projekty są na tyle małe, że mam dobre pojęcie o całej architekturze - w dużym zespole lub bardzo dużym projekcie, automatyczne testy mogą być bardziej przydatne, ponieważ wtedy żaden pojedynczy programista niekoniecznie byłby w stanie wyczuć zapach w miejscu, w którym powinien testować, aby uniknąć regresji.
Ben Lee,
Czy pomijasz etap refaktoryzacji?
rjnilsson
2

Jedną z sytuacji, w której naprawdę przeszkadza mi testowanie, jest to, że chcę szybko wypróbować jakiś pomysł i sprawdzić, czy może on zadziałać, zanim napiszę odpowiednią implementację.

Moje podejście to zwykle:

  1. Zaimplementuj coś, co działa (dowód koncepcji).
  2. Jeśli to działa, skonsoliduj, dodając testy, ulepszając projekt, refaktoryzując.

Czasami nie przechodzę do kroku 2.

W tym przypadku korzystanie z TDD okazało się mieć dla mnie więcej wad niż zalet:

  • Pisanie testów podczas wdrażania dowodu koncepcji spowalnia mnie i przerywa przepływ myśli: chcę zrozumieć pomysł i nie chcę marnować czasu na testowanie szczegółów mojej pierwszej szorstkiej implementacji.
  • Dowiedzieć się może, czy mój pomysł jest coś wart, czy nie.
  • Jeśli okaże się, że pomysł był bezużyteczny, muszę wyrzucić zarówno mój kod, jak i ładnie napisane testy jednostkowe.

Tak więc, kiedy muszę odkrywać nowe pomysły, nie używam TDD i wprowadzam testy jednostkowe tylko wtedy, gdy mam wrażenie, że nowy kod się gdzieś znajduje.

Giorgio
źródło
1
Wygląda na to, że mylisz kod prototypowy z kodem użytecznym. Kod prototypowy to kod testowy . Nie trzeba go testować i nie należy tworzyć testów, które działają z nim. Krok, za którym tęsknisz, to między 1. a 2. .: mówisz „konsoliduj przez pisanie testów”. Problem polega na tym, że nie masz czegoś do skonsolidowania, ale coś do napisania. Zaplanuj przepisanie prototypowego kodu, nie planuj go ponownie używać. Ponowne użycie pozostawia wiele miejsca na kompromis. Przepisywanie formalizuje podział na fazę eksploracji i fazę „kodu jakości”.
utnapistim
3
@utnapistim: Nie mylę kodu prototypowego z kodem użytecznym, raczej fanatycy TDD mylą je i sugerują, że należy używać TDD również do kodu prototypowego. A raczej zakładają, że w ogóle nie ma kodu prototypowego. Zgadzam się również z tobą, że często musisz przepisać, kiedy przechodzisz z prototypu do prawdziwej implementacji. Czasami możesz ponownie użyć części prototypowego kodu, ale musisz być gotowy do przepisania. Naprawdę musisz decydować od przypadku do przypadku.
Giorgio
3
@utnapistim: Zobacz także odpowiedź luis.espinal: „Największą wadą, jaką widziałem, jest nie sama TDD, ale praktykujący. Przyjmują podejście dogmatyczne i gorliwe, w którym wszystko musi zostać przetestowane.”
Giorgio
1

Wady lub koszty TDD

Uwaga: Istnieje szereg różnych rodzajów TDD. Bez względu na jednostkę, BDD, ATDD lub inne warianty wiele trudności pozostaje

Skutki uboczne

Niezależnie od tego, czy chodzi o kpiny, testy czy testy funkcjonalne, zależności od stanów zewnętrznych lub systemów są często źródłem największej złożoności testów, nieporozumień podczas testowania i największego ryzyka popełnienia błędu. Kilka problemów, które widziałem:

  • Kpiny: zapomnij o ustaleniu kolejności połączeń
  • Kpiny: kpina nie pasuje do prawdziwego połączenia lub odpowiedzi
  • Fixture: test opiera się na nierealistycznych danych, maskując inne problemy
  • Urządzenie: przetestuj niemożliwy stan w produkcji
  • Funkcjonalne: fałszywa kompilacja psuje się, ponieważ system zależny jest tymczasowo niedostępny
  • Funkcjonalny: szybkość testu jest bardzo wolna

Będziesz musiał zmienić swoje podejście do kodowania, dla niektórych będzie to drastyczna zmiana.

Różni ludzie kodują na różne sposoby. W TDD musisz zacząć od testu, który potwierdza określone zachowanie, a następnie zaimplementować, aby test przeszedł pomyślnie. Widziałem i byłem programistą, którego programowanie nie sprzyjało TDD. Zajęło mi około 2 miesięcy, kiedy początkowo przyzwyczaiłem się do zmiany podejścia do programowania.

Zrozumienie, na czym polega testowanie, a na czym nie zależy, zajmuje trochę czasu.

Każdy zespół powinien podjąć wyraźną decyzję dotyczącą tego, gdzie chce wytyczyć linię testową. Jakie rzeczy cenią, które chcą przetestować, a czego nie. Często jest to bolesny proces uczenia się, jak pisać dobre testy i co tak naprawdę zależy Ci na testowaniu. W międzyczasie kod będzie w ciągłym przepływie, dopóki nie będzie spójny styl i podejście.

Specyficzny dla testu jednostkowego: duże reaktory

Duży lub fundamentalny reaktor znaczącej bazy kodu z dziesiątkami tysięcy testów jednostkowych wygeneruje ogromny koszt aktualizacji wszystkich testów. Przejawia się to często w wypychaniu się przeciw robieniu refaktora, nawet jeśli jest to właściwe, ponieważ koszty są związane z robieniem tego.

dietbuddha
źródło
0

Moja analogia to bariery na torze Scalextric. Jeśli je założysz, będziesz o wiele mniej ostrożny.

Ludzie mają również trochę miejsca na kadet na temat swoich testów - ponieważ działają dobrze, wierzą, że kod jest w pełni przetestowany, a to dopiero początek procesu testowania.

Moim zdaniem TDD jest odskocznią dla BDD. Wiele testów, które się uruchamiają, tak naprawdę nie pomaga deweloperom, nie wiedząc, co robią testy. W przypadku BDD dane wyjściowe testu są w języku angielskim, co dokumentuje testy, a tym samym buduje zrozumienie systemu.

Robbie Dee
źródło
-1

Zaletą TDD jest to, że zmusza cię do strzeżenia kodu przed ludźmi, którzy go nie rozumieją. Tak, często dotyczy to Ciebie. Ale co się stanie, gdy kod nie jest wart ochrony? Jest dużo kodu, którego w ogóle nie powinno być! Problem z TDD dotyczy więc programistów piszących zły kod. TDD prawdopodobnie nie pomoże im napisać dobrego kodu, jest o wiele bardziej prawdopodobne, że napiszą też okropne testy. Zatem w ich przypadku TDD doda tylko bałagan; źle napisane i / lub zbędne testy nie są bardziej zabawne niż inne formy złego kodu.

Johan
źródło
1
Jeśli sam nie rozumiesz własnego kodu, jak garść spośród miliardów możliwych przypadków testowych może uchronić się przed błędnym kodem?
Michael Shaw
2
Ponieważ zrozumiałeś to, kiedy to napisałeś, ale po drodze o tym zapomniałeś?
Johan
+1 TDD nie chroni przed deweloperem, który źle zrozumiał wymagania biznesowe. W tym miejscu pojawia się BDD ...
Robbie Dee