Poczucie testów jednostkowych bez TDD

28

Rozpoczęliśmy nowy (dość duży) projekt, który planowaliśmy rozwijać przy użyciu TDD.

Pomysł TDD zawiódł (wiele powodów biznesowych i niezwiązanych z biznesem), ale teraz rozmawiamy - czy powinniśmy pisać testy jednostkowe, czy nie. Mój przyjaciel mówi, że pisanie testów jednostkowych bez TDD nie ma sensu (lub jest bliski zeru), powinniśmy skupić się tylko na testach integracyjnych. Uważam, że jest wręcz przeciwnie, że pisanie prostych testów jednostkowych ma pewien sens, aby kod był bardziej odporny na przyszłość. Co myślisz?

Dodano: Myślę, że to nie jest duplikat >> tego pytania << - Rozumiem różnicę między UT a TDD. Moje pytanie nie dotyczy różnic , ale sensu pisania testów jednostkowych bez TDD.

ex3v
źródło
22
Ciekawe, jakie jest uzasadnienie twojego absurdalnego stanowiska ...
Telastyn
11
Założę się, że zdecydowana większość projektów z niektórymi testami jednostkowymi nie używa TDD.
Casey
2
Jakie będą twoje poziomy integracji? Jakie będą twoje jednostki? Jak często refaktoryzujesz poniżej poziomu testowego? Jak szybko przebiegną testy integracyjne? Jak łatwo będą pisać? Ile przypadków kombinatorycznych generują różne części kodu? itp ... Jeśli nie znasz odpowiedzi na te pytania, być może jest jeszcze za wcześnie, aby podejmować zdecydowane decyzje. Czasami TDD jest świetny. Czasami korzyści są mniej wyraźne. Czasami testy jednostkowe są niezbędne. Czasami przyzwoity zestaw testów integracyjnych kupuje prawie tyle samo i jest o wiele bardziej elastyczny ... Zachowaj opcje otwarte.
topo Przywróć Monikę
2
Jako praktyczna rada z doświadczenia, nie testuj wszystkiego, jeśli nie robisz TDD. Ustal, jakie testy są cenne. Przekonałem się, że testy jednostkowe na czystych metodach wejścia / wyjścia są niezwykle cenne, podczas gdy testy integracyjne na bardzo wysokim poziomie aplikacji (np. Wysyłanie żądań internetowych do aplikacji internetowej) są również niezwykle cenne. Uważaj na testy integracyjne średniego poziomu i testy jednostkowe, które wymagają wielu próbnych konfiguracji. Obejrzyj także ten film: youtube.com/watch?v=R9FOchgTtLM .
jpmc26,
Twoja aktualizacja nie ma sensu w odniesieniu do zadanego pytania. Jeśli rozumiesz różnicę między TDD a testami jednostkowymi, to co powstrzymuje cię przed pisaniem testów jednostkowych. Głosowałem za pozostawieniem twojego pytania zamkniętym, chociaż mogłem zobaczyć argument za zamknięciem jako „niejasne, o co pytasz” zamiast duplikatu.

Odpowiedzi:

52

TDD stosuje się głównie (1) w celu zapewnienia zasięgu, (2) oraz do prowadzenia łatwej do utrzymania, zrozumiałej, testowalnej konstrukcji. Jeśli nie korzystasz z TDD, nie uzyskasz gwarancji pokrycia kodu. Ale to w żaden sposób nie oznacza, że ​​powinieneś porzucić ten cel i beztrosko żyć przy zerowym pokryciu.

Testy regresji wymyślono z jakiegoś powodu. Powodem jest to, że na dłuższą metę oszczędzają one więcej czasu na zapobieganiu błędom, niż podejmują dodatkowy wysiłek pisania. Zostało to udowodnione w kółko. Dlatego też, jeśli nie jesteś poważnie przekonani, że organizacja jest dużo, dużo lepiej w inżynierii oprogramowania niż wszystkich guru, którzy polecają testów regresji (lub jeśli plan spada bardzo szybko, tak, że nie ma żadnego dłuższą metę dla Ciebie), tak, powinien bezwzględnie przejść testy jednostkowe, właśnie z tego powodu, który dotyczy praktycznie każdej innej organizacji na świecie: ponieważ wychwytują błędy wcześniej niż testy integracyjne, a to pozwala zaoszczędzić pieniądze. Nie pisanie ich jest jak przekazywanie darmowych pieniędzy po prostu leżących na ulicy.

Kilian Foth
źródło
12
„Jeśli nie korzystasz z TDD, nie uzyskasz gwarancji pokrycia kodu.”: Nie sądzę. Możesz rozwijać się przez dwa dni, a przez następne dwa dni piszesz testy. Ważne jest to, że nie uznajesz funkcji za zakończoną, dopóki nie uzyskasz pożądanego zasięgu kodu.
Giorgio
5
@DougM - Może w idealnym świecie ...
Telastyn
7
Niestety TDD idzie w parze z kpiną i dopóki ludzie nie przestaną tego robić, wszystko wskazuje na to, że test przebiega szybciej . TDD nie żyje. Długie testy na żywo.
MickyD,
17
TDD nie gwarantuje zasięgu kodu. To niebezpieczne założenie. Możesz kodować testy, przechodzić te testy, ale nadal mieć przypadki brzegowe.
Robert Harvey
4
@MickyDuncan Nie jestem do końca pewien, czy całkowicie rozumiem twoje obawy. Wyśmiewanie jest całkowicie prawidłową techniką stosowaną do izolowania jednego komponentu od innych, dzięki czemu testy zachowania tego komponentu można wykonywać niezależnie. Tak, doprowadzone do skrajności, może prowadzić do nadmiernie zaprojektowanego oprogramowania, ale podobnie jak każda technika programistyczna, jeśli zostanie użyta niewłaściwie. Poza tym, jak stwierdza DHH w cytowanym przez ciebie artykule, pomysł użycia tylko pełnych testów systemowych jest równie zły, jeśli nie wręcz gorszy. Ważne jest, aby użyć oceny, aby zdecydować, jaki jest najlepszy sposób testowania jakiejkolwiek konkretnej funkcji .
Jules
21

Mam odpowiednią anegdotę z czegoś, co się teraz dla mnie dzieje . Jestem przy projekcie, który nie korzysta z TDD. Nasi pracownicy QA kierują nas w tym kierunku, ale jesteśmy małym strojem i był to długi, przeciągły proces.

W każdym razie ostatnio korzystałem z biblioteki innej firmy do wykonania określonego zadania. Wystąpił problem związany z korzystaniem z tej biblioteki, więc zostałem zmuszony do napisania wersji tej samej biblioteki na własną rękę. W sumie było to około 5000 wierszy kodu wykonywalnego i około 2 miesięcy mojego czasu. Wiem, że linie kodu są kiepską miarą, ale dla tej odpowiedzi uważam, że jest to przyzwoity wskaźnik wielkości.

Potrzebna mi była jedna szczególna struktura danych, która pozwoliłaby mi śledzić dowolną liczbę bitów. Ponieważ projekt jest w Javie, wybrałem Javę BitSeti trochę ją zmodyfikowałem (potrzebowałem także możliwości śledzenia wiodących 0s, czego BitSet Java nie robi z jakiegoś powodu .....). Po osiągnięciu ~ 93% zasięgu zacząłem pisać testy, które w rzeczywistości podkreśliłyby napisany przeze mnie system. Musiałem przeprowadzić analizę porównawczą niektórych aspektów funkcjonalności, aby upewnić się, że będą one wystarczająco szybkie dla moich końcowych wymagań. Nic dziwnego, że jedna z funkcji zastąpionych przez BitSetinterfejs była absurdalnie powolna w przypadku dużych zestawów bitów (w tym przypadku setek milionów bitów). Inne zastąpione funkcje opierały się na tej jednej funkcji, więc była to ogromna szyjka butelki.

Skończyło się na tym, że poszedłem do deski kreślarskiej i wymyśliłem sposób manipulowania podstawową strukturą BitSet, którą jest long[]. Zaprojektowałem algorytm, poprosiłem kolegów o ich dane wejściowe, a potem zabrałem się za pisanie kodu. Następnie przeprowadziłem testy jednostkowe. Niektóre z nich się zepsuły, a te, które wskazały mi dokładnie, gdzie muszę sprawdzić algorytm, aby go naprawić. Po naprawieniu wszystkich błędów z testów jednostkowych mogłem powiedzieć, że funkcja działa tak, jak powinna. Przynajmniej mogłem być tak pewny, że ten nowy algorytm działał tak samo, jak poprzedni algorytm.

Oczywiście nie jest to kuloodporny. Jeśli w moim kodzie jest błąd, którego testy jednostkowe nie sprawdzają, nie będę tego wiedział. Ale oczywiście ten sam błąd mógł również występować w moim wolniejszym algorytmie. Jednakże , mogę powiedzieć z dużą dozą pewności, że nie trzeba się martwić o złym wyjściem z danej funkcji. Wcześniej istniejące testy jednostkowe pozwoliły mi zaoszczędzić godziny, a może nawet dni, na przetestowanie nowego algorytmu, aby upewnić się, że jest poprawny.

To jest sens przeprowadzania testów jednostkowych niezależnie od TDD - to znaczy, testy jednostkowe zrobią to dla ciebie zarówno w TDD, jak i poza TDD tak samo, kiedy zakończysz refaktoryzację / utrzymanie kodu. Oczywiście należy to połączyć z regularnymi testami regresji, testami dymu, testami rozmytymi itp., Ale testy jednostkowe , jak sama nazwa wskazuje, testują rzeczy na najmniejszym możliwym poziomie atomowym, co daje wskazówki, gdzie pojawiały się błędy.

W moim przypadku bez istniejących testów jednostkowych musiałbym w jakiś sposób wymyślić metodę zapewniającą działanie algorytmu przez cały czas. Co w końcu ... brzmi jak testowanie jednostkowe , prawda?

Shaz
źródło
7

Możesz podzielić kod z grubsza na 4 kategorie:

  1. Proste i rzadko się zmienia.
  2. Proste i często się zmienia.
  3. Złożone i rzadko się zmienia.
  4. Złożone i często się zmienia.

Testy jednostkowe stają się bardziej wartościowe (prawdopodobnie wychwytują ważne błędy) w dalszej części listy. W moich osobistych projektach prawie zawsze robię TDD w kategorii 4. W kategorii 3 zwykle robię TDD, chyba że ręczne testowanie jest prostsze i szybsze. Na przykład kod antyaliasingu byłby trudny do napisania, ale znacznie łatwiejszy do zweryfikowania wizualnego niż napisanie testu jednostkowego, więc test jednostkowy byłby dla mnie tego wart, gdyby ten kod często się zmieniał. Resztę kodu poddaję testowi jednostki dopiero po znalezieniu błędu w tej funkcji.

Czasami trudno jest z góry wiedzieć, do której kategorii pasuje określony blok kodu. Wartością TDD jest to, że przypadkowo nie przegapisz żadnego złożonego testu jednostkowego. Koszt TDD to cały czas spędzany na pisaniu prostych testów jednostkowych. Jednak zwykle ludzie doświadczeni w projekcie wiedzą z wystarczającą pewnością, do jakiej kategorii pasują różne części kodu. Jeśli nie robisz TDD, powinieneś przynajmniej spróbować napisać najcenniejsze testy.

Karl Bielefeldt
źródło
1
Pracując nad kodem, jak sugerujesz na przykładzie antyaliasingu, uważam, że najlepszą rzeczą jest eksperymentalne opracowanie kodu, a następnie dodanie testów charakteryzujących, aby upewnić się, że nie złamię algorytmu później. Testy charakteryzacyjne są bardzo szybkie i łatwe do opracowania, więc narzut związany z robieniem tego jest bardzo niski.
Jules
1

Niezależnie od tego, czy są to testy jednostkowe, komponentowe, integracyjne czy akceptacyjne, ważną częścią jest to, że należy je zautomatyzować. Brak automatycznych testów jest fatalnym błędem dla dowolnego oprogramowania, od prostych CRUD po najbardziej złożone obliczenia. Powodem jest to, że napisanie automatycznych testów zawsze będzie kosztowało mniej niż ciągła potrzeba ręcznego uruchamiania wszystkich testów, gdy tego nie robisz, według rzędów wielkości. Po ich napisaniu wystarczy nacisnąć przycisk, aby sprawdzić, czy przejdą, czy nie. Ręczne testy zawsze będą trwać długo i będą zależeć od ludzi (żywe stworzenia, które się nudzą, mogą nie zwracać na siebie uwagi itd.), Aby móc sprawdzić, czy testy przejdą, czy nie. Krótko mówiąc, zawsze pisz zautomatyzowane testy.

O przyczynie, dla której twój kolega może być przeciwny przeprowadzaniu jakichkolwiek testów jednostkowych bez TDD: Prawdopodobnie dlatego, że trudniej jest zaufać testom napisanym po kodzie produkcyjnym. A jeśli nie możesz zaufać automatycznym testom, są one nic nie warte . Po zakończeniu cyklu TDD musisz najpierw sprawić, że test zakończy się niepowodzeniem (z właściwego powodu), aby móc napisać kod produkcyjny, aby go przejść (i nie więcej). Ten proces zasadniczo testuje twoje testy, więc możesz im zaufać. Nie wspominając już o tym, że pisanie testów przed rzeczywistym kodem popycha cię do zaprojektowania jednostek i komponentów, aby były łatwiejsze do testowania (wysoki poziom odsprzęgania, zastosowane SRP itp.). Chociaż oczywiście robienie TDD wymaga dyscypliny .

Zamiast tego, jeśli najpierw napiszesz cały kod produkcyjny, kiedy napiszesz dla niego testy, będziesz oczekiwać, że przejdą one przy pierwszym uruchomieniu. Jest to bardzo problematyczne, ponieważ mogłeś stworzyć test, który obejmuje 100% twojego kodu produkcyjnego, bez zapewnienia poprawnego zachowania (może nawet nie wykonywać żadnych asercji! Widziałem, jak to się dzieje ), ponieważ nie widzisz, że się nie udaje najpierw sprawdź, czy nie działa z właściwego powodu. Dlatego możesz mieć fałszywie pozytywne wyniki. Fałszywe alarmy ostatecznie zepsują zaufanie do twojego zestawu testów, co w gruncie rzeczy zmusi ludzi do ponownego skorzystania z testów ręcznych, więc poniesiesz koszty obu procesów (pisanie testów + testy ręczne).

Oznacza to, że musisz znaleźć inny sposób na przetestowanie testów , tak jak robi to TDD. Aby móc zaufać testom, uciekaj się do debugowania, komentowania części kodu produkcyjnego itp. Problem polega na tym, że proces „testowania testów” przebiega w ten sposób znacznie wolniej. Dodając ten czas do czasu, w którym ręcznie spędzisz przeprowadzanie testów ad-hoc (ponieważ nie masz testów automatycznych podczas kodowania kodu produkcyjnego), z mojego doświadczenia wynika, że ​​ogólny proces jest znacznie wolniejszy niż ćwiczenie TDD „według książki” (Kent Beck - TDD według przykładu). Ponadto jestem gotów postawić zakład i powiedzieć, że naprawdę „testowanie testów” po ich napisaniu wymaga znacznie więcej dyscypliny niż TDD.

Być może więc Twój zespół może ponownie rozważyć „powody biznesowe i niezwiązane z działalnością gospodarczą” związane z niezastosowaniem się do TDD. Z mojego doświadczenia wynika, że ​​ludzie myślą, że TDD działa wolniej w porównaniu do pisania testów jednostkowych po zakończeniu kodu. To założenie jest błędne, jak czytałeś powyżej.

MichelHenrich
źródło
0

Często jakość testów zależy od ich pochodzenia. Jestem regularnie winny, że nie robię „prawdziwego” TDD - piszę kod, aby udowodnić, że strategia, której chciałbym użyć, faktycznie działa, a następnie opisuję każdy przypadek, który kod ma następnie wspierać w testach. Zwykle jednostką kodu jest klasa, która daje ogólny obraz tego, ile pracy z przyjemnością wykonam bez pokrycia testowego - to znaczy niezbyt dużej ilości. Oznacza to, że semantyczne znaczenie testów dobrze pasuje do testowanego systemu w jego stanie „gotowym” - ponieważ napisałem je, wiedząc, jakie przypadki spełnia SUT i jak je spełnia.

I odwrotnie, TDD ze swoją polityką agresywnego refaktoryzacji ma tendencję do przestarzałych testów co najmniej tak szybko, jak tylko można je napisać, jak publiczny interfejs testowanego systemu. Osobiście uważam, że obciążenie pracą umysłową zarówno projektowania jednostek funkcjonalnych aplikacji, jak i utrzymywania semantyki testów, które obejmują synchronizację, jest zbyt wysokie, aby utrzymać moją koncentrację, a konserwacja testów często spada. Baza kodów kończy się testami, które nie sprawdzają niczego wartościowego lub są po prostu błędne. Jeśli masz dyscyplinę i zdolności umysłowe do aktualizowania zestawu testów, to na pewno ćwicz trening TDD tak rygorystycznie, jak chcesz. Nie wiem, dlatego z tego powodu uważam, że jest mniej skuteczny.

Tom W.
źródło
0

Właściwie wujek Bob wspomniał o bardzo interesującym punkcie w jednym ze swoich filmów o Clean Coders. Powiedział, że cykl Red-Green-Refactor można zastosować na 2 sposoby.

1. to konwencjonalny sposób TDD. Napisz test zakończony niepowodzeniem, a następnie zdaj test i refaktoryzuj.

Drugi sposób polega na napisaniu bardzo małego fragmentu kodu produkcyjnego i natychmiastowym sprawdzeniu przez test jednostkowy, a następnie refaktoryzacji.

Chodzi o to, aby iść bardzo małymi krokami. Oczywiście tracisz weryfikację z kodu produkcyjnego, że twój test zmienił kolor z czerwonego na zielony, ale w niektórych przypadkach, w których pracowałem głównie z młodszymi programistami, którzy odmówili nawet zrozumienia TDD, okazało się to nieco skuteczne.

Powtarzam ponownie (i to podkreślił wujek Bob), chodzi o to, aby przejść bardzo małe kroki i natychmiast przetestować właśnie dodany kod produkcyjny.

Songo
źródło
„... chodzi o to, aby przejść bardzo małymi krokami i od razu przetestować dodany właśnie kod produkcyjny.”: Nie zgadzam się. To, co opisujesz, jeśli jest dobre, gdy masz już jasne pojęcie o tym, co chcesz zrobić i chcesz popracować nad szczegółami, ale najpierw musisz uzyskać duży obraz. W przeciwnym razie, przechodząc bardzo małymi krokami (testuj, rozwijaj, testuj, rozwijaj) możesz zagubić się w szczegółach. „Jeśli nie wiesz, dokąd zmierzasz, możesz się nie dostać.”
Giorgio