TDD, nowe testy, podczas gdy stare nie zostały jeszcze zaimplementowane

13

Eksperymentuję z programowaniem opartym na testach i odkryłem, że często dochodzę do następującej sytuacji:

  1. Piszę testy dla niektórych funkcji X. Testy te kończą się niepowodzeniem.
  2. Próbując zaimplementować X, widzę, że muszę zaimplementować jakąś funkcję Y w dolnej warstwie mojego kodu. Więc...
  3. Piszę testy dla Y. Teraz oba testy dla X i Y kończą się niepowodzeniem.

Kiedyś miałem jednocześnie 4 funkcje na różnych warstwach kodu i traciłem koncentrację na tym, co robię (zbyt wiele testów kończy się niepowodzeniem w tym samym czasie).

Myślę, że mógłbym to rozwiązać, wkładając więcej wysiłku w planowanie moich zadań, jeszcze zanim zacznę pisać testy. Ale w niektórych przypadkach nie wiedziałem, że będę musiał zejść głębiej, ponieważ np. Nie znałem zbyt dobrze API dolnej warstwy.

Co powinienem zrobić w takich przypadkach? Czy TDD ma jakieś zalecenia?

liori
źródło

Odpowiedzi:

9

Dobrą rzeczą jest to, że zdajesz sobie sprawę, że testowany kod wymaga pomocy. Zamiast wdrożyć go od razu, utwórz interfejs i użyj próbnych testów, aby upewnić się, że twoje testy tagują poprawny kod. Po przejściu testów możesz przejść do implementacji kodu, na którym się opiera.

Michael Brown
źródło
Moje testy zwykle nie mają wiedzy na temat tego, co powinna zrobić metoda wewnętrznie (np. Jaki interfejs API niższego poziomu wywołać). Czy powinienem po prostu dostosować testy, aby wyśmiewać to, czego potrzebuję w testowanym kodzie?
liori
2
Podobnie, twoje przetestowane klasy nie powinny obchodzić się, co robią „niższe warstwy”. Użyj mocków / kodów pośredniczących zamiast rzeczywistych klas / obiektów. Może to wymagać nieco więcej wysiłku przy projektowaniu, ale powoduje, że kod jest mniej sprzężony i łatwiejszy do ponownego użycia.
Mchl,
1
Czy używasz wstrzykiwania zależności? W ten sposób możesz łatwo oddzielić problemy niższego poziomu od klas wyższego poziomu. Testowana klasa ma konstruktor z parametrami dla jej zależności (jako interfejsów). W teście tworzysz symulacje interfejsów. Zasadniczo udajesz, że już zaimplementowałeś usługi niższego poziomu.
Michael Brown
@ Mike Brown, tak, tak. Wiem, że mogę tworzyć fałszywe obiekty. Ale potem w teście funkcji Xmuszę wiedzieć, jaką część zależności Xmuszę wyśmiewać. Wydaje mi się, że jest to część szczegółów implementacji, które nie powinny być częścią testów - w przeciwnym razie może być konieczna zmiana testów podczas refaktoryzacji implementacji. Czy powinienem się tym martwić?
liori
1
Wcale nie ... testy powinny odzwierciedlać założenia testowanego systemu. Pomaga także ujawnić to, czego potrzebujesz od usług, na których opiera się system. Zgadzałem się z tobą w tej sprawie, ale porównuję to do tego, jak zrozumiałem programowanie rekurencyjne. Najpierw napisz kod, zakładając, że masz funkcję, która robi to, co chcesz. Następnie piszesz kod, który robi, co chcesz.
Michael Brown,
4

Stuby i symulacje można wykorzystać do symulacji funkcjonalności, która nie jest jeszcze modyfikowana / wdrażana. Mogą również pomóc w rozwiązaniu zależności, które powodują tego rodzaju „reakcję łańcuchową”.

Z drugiej strony najlepszym rozwiązaniem może być utrzymanie tylko jednego (nieudanego) testu, który doprowadzi do następnej zmiany.

Inne testy ukierunkowane na kod, który opiera się na nowej funkcjonalności, mogą zostać tymczasowo wyłączone, ponieważ w tym momencie nie są tak naprawdę istotne. w twoim przypadku wyłącz testy X, dopóki nie zaimplementujesz Y itp.

W ten sposób możesz skupić się na następnej zmianie, która jest tym, czego chcesz, tak myślę.

ratkok
źródło
Ha, szukałem funkcji wyłączającej test podczas testu w moim IDE i nie znalazłem go. Teraz odkryłem, że Python unittestjuż pomija test. To może mi wystarczyć.
liori
Używamy google test C ++ Framework - i ma opcję wyłączenia testów. Wyłączone testy nie są wykonywane, ale kompilowane - w chwili, gdy są potrzebne - są gotowe do uruchomienia (dodatkowo można „wymusić wykonanie” wyłączonych testów - rodzaj „włączania w czasie wykonywania”) - doskonała funkcja ...
ratkok
3

Zatrzymać

Z drugiej strony wygląda na to, że mogą tu występować dwa osobne problemy:

  1. zapomniałeś niektórych historii i scenariuszy testowych i nie odkryłeś ich, dopóki nie zacząłeś pracować nad konkretnym scenariuszem testowym i / lub

  2. faktycznie przeprowadzasz testy jednostkowe, a nie testy funkcji TDD

W przypadku nr 1 zatrzymaj się , wróć i zaktualizuj historie i przetestuj scenariusze, a następnie zacznij od nowa z innym scenariuszem.

W przypadku nr 2 zatrzymaj się i pamiętaj, że testujesz funkcje, a nie jednostki, więc zastosuj symulacje, aby przeświecać inne interfejsy i / lub zaimplementuj więcej kodu, aby test przeszedł pomyślnie bez dodawania nowych scenariuszy testowych. Zakłada się, że nie brakuje Ci scenariuszy testowych, ale zamiast tego - i to jest naprawdę powszechne - łączenie testów jednostkowych i TDD.

Steven A. Lowe
źródło
Naprawdę podoba mi się twoja odpowiedź, lepiej wyjaśnić, co się naprawdę dzieje.
wałek klonowy
... Mając to na uwadze, nie znam premiera na świecie, który nie straciłby rozumu na zdanie: „STOP, musimy się wycofać”. Będą próbować wszystkiego, oprócz poświęcenia swoich pierworodnych przy ołtarzu, aby kontynuować projekt, zadłużenie techniczne i niekompletne testy jednostkowe będą przeklęte. Myślę, że nie można ich winić, gdy ich jedyną miarą w organizacji jest terminowe wykonanie projektu. Niektóre organizacje po prostu cenią czas nad jakością i dlatego prawdopodobnie nigdy nie widziałem, by TDD działało z powodzeniem w tego typu organizacjach, co jest niestety WIĘKSZOŚCIĄ z IMO.
wałek klonowy
@maple_shaft: czas, przez który przestajesz się przegrupowywać, może wynosić zaledwie kilka godzin - chyba że twój proces jest zbyt odległy od bazy, w którym to przypadku zatrzymanie się na kilka dni, aby przywrócić go na właściwe tory, znacznie zwiększy prawdopodobieństwo, że: projekt się powiedzie. Nie ma sensu iść pełną parą na niewłaściwym torze!
Steven A. Lowe
0

To świetne pytanie i OGROMNA frustracja dla mnie również z TDD. Wydaje mi się, że brakuje TDD w tym scenariuszu, w którym po prostu nie masz pojęcia, jakie komponenty lub funkcje niższego poziomu będą potrzebne, dopóki nie zaczniesz opracowywać.

Osobiście odkryłem, że TDD działa tylko wtedy, gdy dokładnie wiesz, co musisz zrobić i do czego zadzwonić, aby wykonać funkcję. Programiści nie zawsze wiedzą wszystko, zanim zaczniemy, więc znalazłem najlepszy sposób na złagodzenie opisanej przez Ciebie sytuacji:

Prototyp

Kiedy tworzę proste prototypowe aplikacje do eksploracji i odkrywania metod i podejść do problemu technicznego, odkrywam dużo pracy nóg i odsuwam te badania na bok, zanim zacznę. Projektowanie i szacowanie stało się również znacznie łatwiejsze.

Jeśli prototyp musi być tak zaangażowany, aby stał się aplikacją, zachęcam cię, abyś nie robił tego leniwie i nie budował testów jednostkowych dla swojego prototypu po fakcie.

W tym momencie powinieneś wiedzieć więcej o interfejsie API niższego poziomu i być w stanie pomyślnie wyśmiewać interfejs API niższego poziomu w komponentach wyższego poziomu.

wałek klonowy
źródło
Więc faktycznie sugerujesz, aby uzyskać więcej informacji na etapie planowania, wykonując kodowanie eksploracyjne w sposób nieformalny (= nie stosując żadnej sformalizowanej metodologii). A następnie załóżmy, że dostarczy wystarczających informacji, aby zaplanować prawdziwy kod. Czy mam rację?
liori
Dlaczego zakładasz, że prototypowanie jest procesem nieformalnym? Każde oszacowanie powinno uwzględniać prototypowanie, a harmonogramy projektu powinny uwzględniać to, a także niezbędne zadanie programistyczne. Widzę to tak samo jak Design lub Code-Review. W tej notatce jest sformalizowany i należy go uwzględnić, jeszcze bardziej w przypadku zadań z wieloma niewiadomymi. Bez prototypowania i możliwości wykonania Proof-of-Concept, a następnie realizacja TDD zakłada, że ​​programiści wiedzą WSZYSTKO o WSZYSTKO ze WSZYSTKIMI funkcjami. Prawdziwy świat nie działa w ten sposób i nie obchodzi mnie, jak jesteś inteligentny lub doświadczony.
wałek klonowy
Przez „nieformalny sposób” nie miałem na myśli, że czas na prototypowanie nie powinien być brany pod uwagę, ale że podczas tworzenia prototypów nie przestrzegasz TDD ani żadnej innej metodologii kodu.
liori
TDD jest metodologią testów jednostkowych i rozwoju. Czy sensowne byłoby zrobienie TDD do przeglądu kodu? Czy TDD ma sens w projektowaniu, pisaniu specyfikacji technicznych lub tablicy? Prototypowanie jest zadaniem samym w sobie, badawczym rodzajem rozwoju badań, dowodu koncepcji i edukacji.
wałek klonowy
1
TDD sprawia doskonały zmysł do prototypowania. Pozwala szybko ujawnić rzeczy, jakie są (cokolwiek, czym są) (obiekt, funkcja, API, cały program) w postaci powtarzalnego, wykonywalnego zestawu wymagań. Zrób sobie przysługę i przeczytaj Growing Object Oriented Software Guided by Tests ; krok po kroku przeprowadzi Cię przez proces tworzenia całej aplikacji (w tym integracji).
Frank Shearar
0

To zależy od tego, jakie testy sprawdzają twoje pisanie podczas TDD.

Klasyczny model polega na pisaniu testów jednostkowych i wykorzystaniu makiet lub kodów pośredniczących w celu oddzielenia testu od innych „jednostek” kodu.

Istnieje wiele innych alternatywnych modeli, takich jak ATDD, w których testuje się pełny stos lub prawie pełny test. W tym konkretnym przypadku twoje testy pisania potwierdzają wymagane zachowanie programu, a nie pojedynczą jednostkę kodu, abyś nie pisał innych testów. Dostaniesz narzędzie w obie strony, aby spełnić test. Następnie dodajesz inne testy dla innych funkcji / zachowań.

dietbuddha
źródło