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.
unit-testing
tdd
ex3v
źródło
źródło
Odpowiedzi:
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.
źródło
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ę
BitSet
i trochę ją zmodyfikowałem (potrzebowałem także możliwości śledzenia wiodących0
s, 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 przezBitSet
interfejs 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ą jestlong[]
. 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?
źródło
Możesz podzielić kod z grubsza na 4 kategorie:
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.
źródło
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.
źródło
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.
źródło
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.
źródło