To może być dość głupie pytanie, ponieważ jestem przy pierwszych próbach TDD. Uwielbiałem poczucie pewności i ogólnie lepszą strukturę mojego kodu, ale kiedy zacząłem stosować go na czymś większym niż przykłady klasowych zabawek, napotkałem trudności.
Załóżmy, że piszesz bibliotekę. Wiesz, co musi zrobić, znasz ogólny sposób jego implementacji (pod względem architektury), ale ciągle „odkrywasz”, że musisz wprowadzać zmiany w publicznym interfejsie API podczas pisania kodu. Być może musisz przekształcić tę prywatną metodę w wzorzec strategii (a teraz musisz zdać kpiącą strategię w swoich testach), być może źle dopełniłeś odpowiedzialności tu i tam i podzieliłeś istniejącą klasę.
Kiedy poprawiasz istniejący kod, TDD wydaje się być naprawdę dobrym dopasowaniem, ale kiedy piszesz wszystko od zera, API, dla którego piszesz testy, jest nieco „rozmyte”, chyba że zrobisz duży projekt z góry. Co robisz, gdy masz już 30 testów metody, której zmieniono podpis (i dla tej części, zachowanie)? To wiele testów, które należy zmienić po dodaniu.
Odpowiedzi:
To, co nazywacie „dużym projektem z góry” , nazywam „rozsądnym planowaniem architektury klasowej”.
Nie można wyhodować architektury na podstawie testów jednostkowych. Nawet wujek Bob tak mówi.
http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf , strona 4
Myślę, że bardziej rozsądne byłoby podejście do TDD z perspektywy walidacji projektu konstrukcyjnego. Skąd wiesz, że projekt jest niepoprawny, jeśli go nie przetestujesz? Jak zweryfikować poprawność wprowadzonych zmian bez zmiany oryginalnych testów?
Oprogramowanie jest „miękkie” właśnie dlatego, że może ulec zmianie. Jeśli nie czujesz się komfortowo w związku z ilością zmian, zdobywaj doświadczenie w projektowaniu architektury, a liczba zmian, które będziesz musiał wprowadzić w architekturze aplikacji, z czasem się zmniejszy.
źródło
Jeśli zrobisz TDD. Nie można zmienić podpisu i zachowania bez przeprowadzenia go przez testy. Tak więc 30 testów, które zakończyły się niepowodzeniem, albo zostało usuniętych w trakcie procesu, albo zostało zmienionych / refaktoryzowanych wraz z kodem. Lub są już przestarzałe, bezpieczne do usunięcia.
Nie możesz zignorować 30-krotnego czerwonego w cyklu refaktora czerwono-zielonego?
Twoje testy powinny być refaktoryzowane wraz z kodem produkcyjnym. Jeśli możesz sobie na to pozwolić, ponownie uruchom wszystkie testy po każdej zmianie.
Nie bój się usunąć testów TDD. Niektóre testy kończą testowanie bloków konstrukcyjnych, aby uzyskać pożądany wynik. Liczy się pożądany wynik na poziomie funkcjonalnym. Testy dotyczące pośrednich kroków w algorytmie, który wybrałeś / wymyśliłeś, mogą, ale nie muszą mieć dużej wartości, gdy istnieje więcej niż jeden sposób na osiągnięcie wyniku lub początkowo wpadłeś w ślepy zaułek.
Czasami możesz stworzyć przyzwoite testy integracyjne, zatrzymać je i usunąć resztę. To w pewnym stopniu zależy od tego, czy pracujesz na lewą stronę, czy od góry i od tego, jak duże kroki wykonujesz.
źródło
Jak powiedział właśnie Robert Harvey, prawdopodobnie próbujesz użyć TDD do czegoś, co powinno być obsługiwane przez inne narzędzie koncepcyjne (to znaczy: „projektowanie” lub „modelowanie”).
Spróbuj zaprojektować (lub „model”) swój system w sposób dość abstrakcyjny („ogólny”, „niejasny”). Na przykład, jeśli trzeba modelować samochód, wystarczy mieć klasę samochodu z niejasną metodą i polem, na przykład startEngine () i int. To znaczy: opisz, co chcesz ujawnić opinii publicznej , a nie jak chcesz to wdrożyć. Spróbuj udostępnić tylko podstawowe funkcje (odczytywanie, pisanie, uruchamianie, zatrzymywanie itp.) I pozostaw na nim rozbudowany kod klienta (preparMyScene (), killTheEnemy () itp.).
Zapisz swoje testy przy założeniu tego prostego publicznego interfejsu.
Zmień wewnętrzne zachowanie swoich klas i metod, kiedy tylko będziesz tego potrzebować.
Jeśli i kiedy musisz zmienić interfejs publiczny i zestaw testów, zatrzymaj się i pomyśl. Najprawdopodobniej jest to znak, że coś jest nie tak w interfejsie API i projekcie / modelowaniu.
Zmiana API nie jest niczym niezwykłym. Większość systemów w wersji 1.0 wyraźnie ostrzega programistów / użytkowników przed możliwymi zmianami w interfejsie API. Mimo to ciągły, niekontrolowany przepływ zmian API jest wyraźną oznaką złego (lub kompletnego braku) projektu.
BTW: Zwykle powinieneś po prostu mieć garść testów dla każdej metody. Metoda z definicji powinna implementować jasno określone „działanie” w odniesieniu do pewnego rodzaju danych. W idealnym świecie powinno to być pojedyncze działanie, które odpowiada pojedynczemu testowi. W prawdziwym świecie nie jest niczym niezwykłym (i nie jest źle) mieć kilka różnych „wersji” tej samej akcji i kilka różnych odpowiednich testów. Na pewno powinieneś unikać 30 testów tej samej metody. Jest to wyraźny znak, że metoda próbuje zrobić zbyt wiele (a jej wewnętrzny kod wymknął się spod kontroli).
źródło
Patrzę na to z perspektywy użytkownika. Na przykład, jeśli twoje interfejsy API pozwalają mi utworzyć obiekt Person o nazwie i wieku, lepiej byłoby mieć konstruktor Person i metody akcesora dla imienia i wieku. Tworzenie przypadków testowych dla nowych Osób z imieniem i wiekiem jest proste.
doug
źródło