TDD: Czy robię to dobrze?


14

Jestem nowym programistą (uczę się tylko od około roku), a moim celem, aby być w tym lepszym, niedawno dowiedziałem się o TDD. Chciałem przyzwyczaić się do używania go, ponieważ wydaje się to bardzo pomocne. Chciałem to sprawdzić i upewnić się, że używam go poprawnie.

Co ja robię:

  1. Pomyśl o nowej metodzie, której potrzebuję.
  2. Utwórz test dla tej metody.
  3. Test negatywny.
  4. Napisz metodę.
  5. Zdać test.
  6. Metoda refaktoryzacji.
  7. Powtarzać.

Robię to dla KAŻDEJ metody, którą piszę, czy są jakieś, z którymi nie powinienem się przejmować? Później zwykle myślę o sposobie przetestowania moich już istniejących metod w inny sposób lub w innej sytuacji. Czy powinienem wykonać te nowe testy, o których myślę, czy skoro każda metoda ma już własny test, czy nie powinienem się tym przejmować? Czy mogę OVER testować mój kod? Myślę, że moim głównym problemem jest zadawanie tego pytania.

EDYTOWAĆ

Zastanawiałem się nad tym. Czy podczas tworzenia czegoś takiego jak GUI, TDD byłoby konieczne w takiej sytuacji? Osobiście nie mogę wymyślić, jak napisałbym na to testy.


5
Robisz to już znacznie lepiej niż doświadczeni profesjonaliści, którzy twierdzą, że wszystko testują (ale nie robią tego).
yannis

To, co opisujesz, nie jest Duchem TDD.

1
Możesz zajrzeć do ATDD lub BDD.
dietbuddha

Być może zacznij wyżej - pomyśl o nowym module, którego potrzebujesz.

Odpowiedzi:


16

To, co opisujesz jako przepływ pracy, nie jest moim zdaniem duchem TDD.

Streszczenie książki Kent Becks na Amazon mówi:

Po prostu rozwój oparty na testach ma na celu wyeliminowanie obaw związanych z tworzeniem aplikacji.Podczas gdy strach jest zdrowy (często postrzegany jako sumienie, które prosi programistów, by „uważali!”), Autor uważa, że ​​produktami ubocznymi strachu są niepewni, zrzędliwi i niekomunikatywni programiści, którzy nie są w stanie przyjąć konstruktywnej krytyki. Kiedy zespoły programistyczne kupują TDD, natychmiast widzą pozytywne wyniki. Eliminują strach związany z ich pracą i są lepiej przygotowani do stawienia czoła trudnym wyzwaniom, przed którymi stają. TDD eliminuje niepewne cechy, uczy programistów komunikowania się i zachęca członków zespołu do szukania krytyki. Jednak nawet autor przyznaje, że zrzędliwość musi być wypracowana indywidualnie! Krótko mówiąc, założeniem TDD jest to, że kod powinien być ciągle testowany i refaktoryzowany.

Praktyczny TDD

Formalne zautomatyzowane testowanie, zwłaszcza testowanie jednostkowe, każda metoda każdej klasy jest równie złym anty-wzorcem i niczego nie testuje. Należy zachować równowagę. Czy piszesz testy jednostkowe dla każdej setXXX/getXXXmetody, one również są metodami!

Testy mogą również pomóc zaoszczędzić czas i pieniądze, ale nie zapominaj, że ich opracowanie i opracowanie kosztuje dużo czasu i pieniędzy. Są one kodem, więc utrzymują czas i pieniądze. Jeśli zanikną z powodu braku utrzymania, stają się zobowiązaniem bardziej niż korzyścią.

Podobnie jak wszystko inne, istnieje równowaga, której nikt inny nie może zdefiniować. Wszelkie dogmaty w obu kierunkach są prawdopodobnie bardziej błędne niż poprawne.

Dobrą miarą jest kod, który ma kluczowe znaczenie dla logiki biznesowej i podlega częstym modyfikacjom w zależności od zmieniających się wymagań. Te rzeczy wymagają zautomatyzowanych testów formalnych, które byłyby dużym zwrotem z inwestycji.

Będziesz bardzo ciężko znaleźć wiele profesjonalnych sklepów, które działają w ten sposób. Po prostu nie ma sensu wydawanie pieniędzy na testowanie rzeczy, które we wszystkich praktycznych celach nigdy się nie zmienią po wykonaniu prostego testu dymu. Pisanie formalnych automatycznych testów jednostkowych dla .getXXX/.setXXXmetod jest doskonałym przykładem tego, kompletnego marnowania czasu.

Minęły dwie dekady, odkąd wskazano, że testy programu mogą w przekonujący sposób wykazać obecność błędów, ale nigdy nie mogą wykazać ich braku. Po zacytowaniu tej dobrze nagłośnionej uwagi inżynier oprogramowania wraca do porządku dnia i nadal udoskonala swoje strategie testowe, podobnie jak dotychczasowy alchemik, który nadal udoskonalał swoje chryzokosmiczne oczyszczenia.

- Edsger W. Djikstra . (Napisany w 1988 roku, więc teraz jest bliżej 4,5 dekad)

Zobacz także tę odpowiedź .


1
To właściwie dotyczy tego, co mnie martwiło. Czułem, że nie powinienem testować każdej metody tak jak byłem, ale nie byłem pewien. Wygląda na to, że nadal będę musiał przeczytać więcej o TDD.
cgasser

@kevincline Większość czasu setXXX/getXXXwcale nie jest potrzebna :)
Chip

1
Kiedy przypomnisz sobie o tym trywialnym getXXX i pomylisz się lub wprowadzisz leniwe ładowanie do getXXX i pomylisz się, wtedy będziesz wiedział, że czasami naprawdę chcesz przetestować swoje gettery.
Frank Shearar

13

Jesteś bardzo blisko. Spróbuj myśleć w ten nieco inny sposób.

  1. Pomyśl o nowym zachowaniu, którego potrzebuję.
  2. Utwórz test dla tego zachowania.
  3. Test negatywny.
  4. Napisz nową lub rozszerz istniejącą metodę.
  5. Zdać test.
  6. Kod refaktora.
  7. Powtarzać.

Nie twórz automatycznie obiektów pobierających i ustawiających dla każdej właściwości . Nie myśl o całej metodzie i napisz test (y), które obejmą całą funkcjonalność . Spróbuj obudować właściwości wewnątrz klasy i napisz metody, aby zapewnić potrzebne zachowanie. Pozwól, aby twoje metody ewoluowały w dobry projekt, zamiast próbować je planować z góry. Pamiętaj, że TDD to proces projektowania, a nie proces testowania. Przewaga nad innymi procesami projektowymi polega na pozostawieniu za sobą strumienia automatycznych testów regresji, a nie na kawałku papieru, który wrzucasz do kosza.

Pamiętaj też o trzech zasadach TDD wuja Boba .

  1. Nie wolno pisać żadnego kodu produkcyjnego, chyba że ma to negatywny wynik pozytywnego testu jednostkowego.
  2. Nie wolno pisać więcej testów jednostkowych niż jest to wystarczające do zaliczenia; awarie kompilacji to awarie.
  3. Nie wolno pisać więcej kodu produkcyjnego, niż jest to wystarczające do zaliczenia jednego z nieudanych testów jednostkowych.

1
@Zexanima: Radzisz sobie znacznie lepiej niż większość z nas po roku. Próbuję tylko wskazać ci następny krok.
pdr

2
Myślę, że te 3 zasady, do których linkujesz; choć idylliczne mogą się wydawać, są wyjątkowo dogmatyczne i bardzo nierealistycznie sztywne w 99% wszystkich sklepów produkcyjnych, z którymi każdy może się spotkać.

1
@FrankShearar lub może to być postrzegane jako niepraktyczne blaknięcie fundamentalistycznego ekstremisty i hurtownie lekceważone. Pracowałem w sklepach, które miały takie dogmatyczne podejście, przyjęły dogmat dosłownie i nie rozumiały sensu; pisanie testów, które nie testowały żadnego z ich rzeczywistych kodów w praktyczny sposób, a kończyło się na testowaniu struktur Mocking i Dependency Injection w celu pomieszania tego, co było w najlepszym razie ważne.

1
@pdr Duch czegoś jest diametralnie przeciwny dogmatycznemu sformalizowanemu kanonizowaniu tej rzeczy. Jedną rzeczą jest mieć filozofię, a drugą przekuć ją w religię . TDD częściej niż nie jest omawiane w czarno-białych dogmatycznych terminach religijnych . To, że 3 zasady brzmią w sposób dogmatyczny i religijny w prezentacji, a to, co się słyszy, to mantra Test, Test, Test , dla kogoś takiego jak OP, biorą je dosłownie i to powoduje więcej szkody niż pożytku. Sprzeciwiłem się Frankowi, że stwierdzenia polaryzacyjne mogą wyrządzić więcej szkody niż przyczynie.

2
Chodziło mi o to, że dogmatyzm bierze się ze ślepego przyjęcia czegoś jako ewangelii . Weź polaryzację, wypróbuj ją, zmusi cię do opuszczenia strefy komfortu. Nie możesz ocenić kompromisów związanych z TDD, jeśli nie spróbujesz ekstremalnego podejścia 3-punktowego-wszystko-albo-nic, ponieważ mieć danych .
Frank Shearar

5

Kilka rzeczy do dodania do odpowiedzi innych:

  1. Istnieje coś takiego jak nadmierne testowanie. Chcesz mieć pewność, że testy jednostkowe nakładają się na siebie w jak najmniejszym stopniu. Nie ma sensu, aby wiele testów weryfikowało te same warunki w tym samym fragmencie kodu. Z drugiej strony, gdy refaktoryzujesz kod produkcyjny i masz wiele testów pokrywających się z tą sekcją, będziesz musiał wrócić i naprawić wszystkie te testy. Natomiast jeśli się nie pokrywają, to jedna zmiana najwyżej przerwie tylko jeden test.

  2. Tylko dlatego, że pomyślałeś o lepszym sposobie napisania testu, nie wrócę tam i nie zacznę go przepisywać. To wraca do osób, które wciąż piszą i przepisują tę samą klasę / funkcję, ponieważ starają się ją doskonalić. To nigdy nie będzie idealne, więc idź dalej. Kiedy odkryjesz lepszą metodę, nie zapominaj o niej (lub dodaj komentarz do testu). Następnym razem, gdy tam będziesz, i zobaczysz natychmiastową korzyść z przejścia na nowy sposób, nadszedł czas na refaktoryzację. W przeciwnym razie, jeśli funkcja jest gotowa, a ty przeszedłeś dalej i wszystko działa, zostaw ją działającą.

  3. TDD koncentruje się na zapewnieniu natychmiastowej wartości, a nie po prostu upewnieniu się, że każda funkcja jest testowalna. Po dodaniu funkcjonalności zacznij od pytania „czego potrzebuje klient”. Następnie zdefiniuj interfejs, aby dać klientowi to, czego potrzebuje. Następnie zaimplementuj wszystko, co potrzebne do zdania testu. TDD jest prawie jak testowanie scenariuszy przypadków użycia (w tym wszystkich „co-jeśli”), a nie po prostu kodowanie funkcji publicznych i testowanie każdego z nich.

  4. Zapytałeś o testowanie kodu GUI. Wyszukaj wzorce „Humble Dialog” i „MVVM”. Ideą obu tych metod jest utworzenie zestawu klas „modelu widoku”, które w rzeczywistości nie mają logiki specyficznej dla interfejsu użytkownika. Jednak klasy te będą miały całą logikę biznesową, która zazwyczaj stanowi część interfejsu użytkownika, i klasy te powinny być w 100% testowalne. To, co zostało, to bardzo cienka powłoka interfejsu użytkownika i tak, zwykle ta powłoka pozostaje bez pokrycia testowego, ale w tym momencie nie powinna mieć prawie żadnej logiki.

  5. Jeśli masz dużą część istniejącego kodu, jak sugerowało niewiele innych, nie powinieneś zaczynać dodawać testów jednostkowych absolutnie wszędzie. Zajmie ci to wieczność i nie odniesiesz korzyści z dodania testów jednostkowych do 80% klas, które są stabilne i nie zmienią się w bliskiej (lub nie tak bliskiej) przyszłości. Jednak w przypadku nowej pracy uważam, że używanie programowania TDD z WSZYSTKIM kodem jest niezwykle korzystne. Po zakończeniu nie tylko otrzymujesz pakiet z automatycznymi testami, ale faktyczny rozwój ma ogromne zalety:

    • Biorąc pod uwagę testowalność, napiszesz kod, który jest mniej sprzężony i bardziej modułowy
    • Rozważając swój kontrakt publiczny przed czymkolwiek innym, skończysz z publicznymi interfejsami, które są znacznie czystsze
    • Podczas pisania kodu weryfikacja nowej funkcjonalności zajmuje milisekundy w porównaniu z uruchomieniem całej aplikacji i próbą wymuszenia wykonania właściwą ścieżką. Mój zespół wciąż wydaje kod obsługi błędów, który nawet nie został WYKONANY RAZ tylko dlatego, że nie udało mu się uzyskać odpowiedniego zestawu warunków. To niesamowite, ile czasu marnujemy, gdy później w QA takie warunki się zdarzają. I tak, wiele z tego kodu jest tym, co ktoś uważałby za „brak obszaru dla wielu zmian w przyszłości po zakończeniu testowania dymu”.

1

Istnieje kilka metod, które nie są testowane, a mianowicie te testy. Jest jednak coś, co można powiedzieć o niektórych testach dodawanych po napisaniu kodu początkowego, takich jak warunki brzegowe i inne wartości, aby możliwe było przeprowadzenie wielu testów na jednej metodzie.

Chociaż możesz nadmiernie przetestować swój kod, zwykle przychodzi to tam, gdzie ktoś chce przetestować każdą możliwą kombinację danych wejściowych, co nie brzmi jak to, co robisz. Na przykład, jeśli masz metodę, która przyjmuje znak, czy piszesz test dla każdej możliwej wartości, którą można wprowadzić? To byłby moment, w którym można by przeprowadzić nadmierny test, IMO.


Ahh, okej. Nie to robię. Po prostu zwykle myślę o innej sytuacji, w której mógłbym przetestować swoje metody znacznie później, po tym, jak przeprowadziłem już ich pierwszy test. Po prostu upewniłem się, że te „dodatkowe” testy były warte wykonania, lub czy to już koniec.
cgasser

Jeśli pracujesz w dostatecznie małych krokach, zwykle możesz mieć pewność, że test rzeczywiście działa. Innymi słowy, niepowodzenie testu (z właściwego powodu!) Samo w sobie testuje test. Ale ten poziom „względnie pewny” nie będzie tak wysoki jak testowany kod.
Frank Shearar,

1

Ogólnie robisz to dobrze.

Testy są kodem. Więc jeśli możesz ulepszyć test, śmiało go przerób. Jeśli uważasz, że test można ulepszyć, przejdź dalej i zmień go. Nie bój się zastąpić testu lepszym.

Zalecam podczas testowania kodu unikaj określania, w jaki sposób kod ma robić to, co robi. Testy powinny sprawdzać wyniki metod. Pomoże to w refaktoryzacji. Niektóre metody nie muszą być jawnie testowane (np. Proste metody pobierające i ustawiające), ponieważ użyjesz ich do weryfikacji wyników innych testów.


Pisałem też testy dla pobierających i ustawiających, więc dziękuję za tę wskazówkę. Pozwoli mi to zaoszczędzić trochę niepotrzebnej pracy.
cgasser

„Niektóre metody nie muszą być jawnie testowane (np. Proste metody pobierające i ustawiające)” - Nigdy nie kopiowałeś / nie wklejałeś metody pobierającej i ustawiającej i nie zapomniałeś zmienić nazwy pola za nią? W prostym kodzie chodzi o to, że wymaga on prostych testów - ile czasu tak naprawdę oszczędzasz?
pdr

Nie chodzi mi o to, że metoda nie jest testowana. Jest to po prostu sprawdzane poprzez potwierdzenie, że ustawione są inne metody lub podczas faktycznego konfigurowania testu. Jeśli moduł pobierający lub ustawiający nie działa poprawnie, test zakończy się niepowodzeniem, ponieważ właściwości nie zostały ustawione poprawnie. Dostajesz je przetestowane za darmo, domyślnie.
Schleis

Testy Gettera i Setera nie trwają długo, więc prawdopodobnie będę je nadal wykonywać. Jednak nigdy nie kopiuję i nie wklejam żadnego z moich kodów, aby nie napotkać tego problemu.
cgasser

0

Moja opinia na temat TDD jest taka, że ​​narzędzia stworzyły świat programistów w stylu „wskaż i kliknij”. To, że narzędzia tworzą odcinek testowy dla każdej metody, nie oznacza, że ​​powinieneś pisać testy dla każdej metody. Niektóre osoby „zmieniają nazwę” TDD na BDD (programowanie oparte na zachowaniu), gdzie testy są o wiele bardziej szczegółowe i mają na celu przetestowanie zachowania klasy, a nie każda skomplikowana metoda.

Jeśli projektujesz swoje testy do testowania klasy zgodnie z przeznaczeniem, zaczniesz czerpać pewne korzyści, zwłaszcza gdy zaczniesz pisać testy, które wykonują nieco więcej niż każdą metodę, zwłaszcza gdy zaczniesz testować interakcję tych metody Podejrzewam, że można by pomyśleć o pisaniu testów dla klasy, a nie metod. W każdym razie nadal musisz napisać „testy akceptacyjne”, które sprawdzają kombinację metod, aby upewnić się, że nie ma sprzeczności ani konfliktów w ich wspólnym stosowaniu.

Nie myl TDD z testowaniem - nie jest. TDD jest zaprojektowane tak, abyś pisał kod, aby wykonać swoje wymagania, a nie testować metody. Jest to subtelna, ale ważna kwestia, często tracona przez osoby, które ślepo piszą kod testowy dla każdej metody. Powinieneś pisać testy, które upewniają się, że kod robi to, co chcesz, a nie, że napisany kod działa tak, jak powinien.

Istnieje kilka dobrych linków po prawej stronie na temat BDD v TDD. Sprawdź je.


0

Kiedy zaczniesz uczyć się TDD, tak, powinieneś ślepo postępować zgodnie z dogmatycznym podejściem, aby nie pisać ani jednego wiersza kodu, z wyjątkiem zaliczenia testu zakończonego niepowodzeniem, i napisania tylko tyle testu, aby się nie powiódł .

Gdy dowiesz się, o co chodzi w TDD, NASTĘPNIE możesz zdecydować, że pewne rzeczy nie są warte testowania. To jest to samo podejście, które powinieneś stosować do wszystkiego, a japońskie sztuki walki nazywają to „ shuhari ”. (Link wyjaśnia również, w jaki sposób można przejść przez etapy uczenia się bez nauczyciela, co jest, jak podejrzewam, sposobem, w jaki większość ludzi musi się uczyć).


0

Uważam, że przesadzasz.

Ćwiczę TDD od wielu lat i z mojego doświadczenia wynika, że ​​kiedy TDD jest wykonywane skutecznie, zyskujesz dwie główne korzyści:

  • Szybka informacja zwrotna
  • Włącz refaktoryzację

Szybka informacja zwrotna

Szczególnie w przypadku języków dynamicznych mogę wykonać odpowiednie testy w mniej niż sekundę. Mam obserwatorów systemu plików, którzy automatycznie uruchamiają te testy po zmianie pliku źródłowego na dysku. Dlatego praktycznie nie mam czasu na testy i od razu wiem, czy kod, który piszę, działał zgodnie z oczekiwaniami. Zatem TDD prowadzi do bardzo wydajnego sposobu pracy.

Włącz refaktoryzację

Jeśli masz dobry zestaw testów, możesz bezpiecznie refaktoryzować, ponieważ zyskujesz nowy wgląd w to, jak system powinien zostać zaprojektowany.

Dobry pakiet testowy pozwala przenieść odpowiedzialność w kodzie i mieć pewność, że kod będzie działał zgodnie z oczekiwaniami po przeniesieniu. I powinieneś być w stanie to zrobić przy niewielkich zmianach w kodzie testowym.

Jeśli napiszesz testy dla każdej metody w twoim systemie, istnieje prawdopodobieństwo, że nie możesz łatwo refaktoryzować kodu, każdy refaktor twojego kodu będzie wymagał ogromnych zmian w kodzie testowym. Czy możesz być pewien, że kod testowy nadal działa zgodnie z oczekiwaniami? A może przypadkowo wprowadziłeś błąd w kodzie testowym, co w konsekwencji prowadzi do błędu w kodzie produkcyjnym?

Jeśli jednak, jak sugeruje odpowiedź pdr , podczas pisania testów skupisz się na zachowaniu zamiast metod , będziesz mieć testy, które będą wymagały znacznie mniej zmian podczas refaktoryzacji systemu.

Lub, jak mówi Ian Cooper w tej prezentacji (cytowałem z pamięci, więc może być niepoprawnie cytowany):

Powodem napisania nowego testu powinno być dodanie nowego zachowania, a nie dodanie nowej klasy


-2

Powinieneś przetestować każdą publiczność metodę.

Problem polega na tym, że jeśli twoje metody publiczne są bardzo małe, prawdopodobnie ujawniasz zbyt wiele informacji. Powszechną praktyką eksponowania każdej nieruchomości jakogetXXX() faktycznie przerywa enkapsulację.

Jeśli twoje metody publiczne są zachowaniem klasy, powinieneś je przetestować. Jeśli nie, nie są to dobre publiczne metody.

EDYCJA: odpowiedź pdr jest znacznie bardziej kompletna niż moja.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.