Wszystkie przykłady, które przeczytałem i widziałem na szkoleniowych filmach, mają uproszczone przykłady. Ale czego nie widzę, jak zrobię „prawdziwy” kod po tym, jak zrobię się zielony. Czy to jest część „Refaktoryzacja”?
Jeśli mam dość skomplikowany obiekt za pomocą złożonej metody i piszę mój test i absolutne minimum, aby go przekazać (po pierwszym niepowodzeniu, czerwony). Kiedy wrócę i napiszę prawdziwy kod? A ile prawdziwego kodu piszę przed ponownym testem? Domyślam się, że ostatnia to więcej intuicji.
Edycja: Dziękujemy wszystkim, którzy odpowiedzieli. Wszystkie twoje odpowiedzi bardzo mi pomogły. Wygląda na to, że istnieją różne pomysły na to, o co pytałem lub o co się myliłem, a może są, ale pytałem o to, powiedz, że mam wniosek o budowę szkoły.
W moim projekcie mam architekturę, od której chcę zacząć, Historie użytkowników itd. Stąd biorę Historie użytkowników i tworzę test, aby przetestować Historię użytkownika. Użytkownik mówi: Mamy osoby zapisujące się do szkoły i płacące opłaty rejestracyjne. Tak więc myślę o tym, jak to zrobić. Robiąc to, projektuję klasę testową dla klasy X (może Student), która zawiedzie. Następnie tworzę klasę „Student”. Może „Szkoła” nie wiem.
Ale w każdym razie Projekt TD zmusza mnie do przemyślenia historii. Jeśli zdołam sprawić, że test się nie powiedzie, wiem, dlaczego się nie powiedzie, ale to zakłada, że mogę go zdać. Chodzi o projektowanie.
Porównuję to do myślenia o rekursji. Rekurencja nie jest trudną koncepcją. Być może trudniej jest zapamiętać to w twojej głowie, ale w rzeczywistości najtrudniejszą częścią jest wiedza, kiedy rekursja „pęka”, kiedy przestać (moim zdaniem, oczywiście). Więc muszę pomyśleć o tym, co się kończy pierwsza rekurencja. Jest to tylko niedoskonała analogia i zakłada, że każda rekurencyjna iteracja jest „zaliczeniem”. Znowu tylko opinia.
We wdrażaniu szkoła jest trudniejsza do zauważenia. Księgi liczbowe i bankowe są „łatwe” w tym sensie, że można użyć prostej arytmetyki. Widzę + b i zwracam 0 itd. W przypadku systemu ludzi muszę się bardziej zastanowić, jak to zaimplementować . Mam pojęcie niepowodzenia, zaliczenia, refaktoryzacji (głównie z powodu studiów i tego pytania).
To, czego nie wiem, opiera się na moim braku doświadczenia. Nie wiem, jak nie udać się zarejestrować nowego studenta. Nie wiem, jak zawieść, gdy ktoś wpisuje nazwisko i jest ono zapisywane w bazie danych. Wiem, jak zrobić +1 dla prostej matematyki, ale z jednostkami takimi jak osoba, nie wiem, czy tylko testuję, aby sprawdzić, czy odzyskam unikalny identyfikator bazy danych lub coś innego, gdy ktoś wpisze nazwę w baza danych lub obie lub żadna.
A może to pokazuje, że wciąż jestem zdezorientowany.
Odpowiedzi:
Nie „wracasz” i nie piszesz „prawdziwego kodu”. To wszystko prawdziwy kod. Powróć i dodaj kolejny test, który zmusza cię do zmiany kodu, aby nowy test przeszedł pomyślnie.
Co do tego, ile kodu piszesz przed ponownym testem? Żaden. Piszesz zerowy kod bez nieudanego testu, który zmusza Cię do napisania większej ilości kodu.
Zauważ wzór?
Przejdźmy przez (kolejny) prosty przykład w nadziei, że to pomoże.
Łatwy peazy.
Nie to, co nazwałbyś prawdziwym kodem, prawda? Dodajmy test, który wymusza zmianę.
Moglibyśmy zrobić coś głupiego
if n == 1
, ale przejdziemy do rozsądnego rozwiązania.Chłodny. Działa to dla wszystkich numerów innych niż FizzBuzz. Jakie są kolejne dane wejściowe, które zmuszą do zmiany kodu produkcyjnego?
I znowu. Napisz test, który jeszcze nie przejdzie.
A teraz omówiliśmy wszystkie wielokrotności trzech (które nie są również wielokrotnościami pięciu, zanotujemy to i wrócimy).
Nie napisaliśmy jeszcze testu „Buzz”, więc napiszmy to.
I znowu wiemy, że jest inny przypadek, którym musimy się zająć.
A teraz możemy obsłużyć wszystkie wielokrotności 5, które nie są również wielokrotnościami 3.
Do tego momentu ignorowaliśmy etap refaktoryzacji, ale widzę pewne powielanie. Wyczyśćmy to teraz.
Chłodny. Teraz usunęliśmy duplikację i stworzyliśmy dobrze nazwaną funkcję. Jaki następny test możemy napisać, aby zmusić nas do zmiany kodu? Cóż, unikaliśmy sytuacji, w której liczba jest podzielna przez 3 i 5. Napiszmy teraz.
Testy są udane, ale mamy więcej powielania. Mamy opcje, ale zamierzam zastosować „Wyodrębnij zmienną lokalną” kilka razy, abyśmy przeredagowali tekst zamiast go przepisywać.
Omówiliśmy każdy rozsądny wkład, ale co z nieuzasadnionym wkładem? Co się stanie, jeśli przejdziemy 0 lub ujemny? Napisz te przypadki testowe.
Czy to zaczyna wyglądać jak „prawdziwy kod”? Co ważniejsze, w którym momencie przestał on być „nierzeczywistym kodem” i przejść do bycia „prawdziwym”? To jest coś do rozważenia ...
Byłem w stanie to zrobić, po prostu szukając testu, o którym wiedziałem, że nie przejdzie go z każdym krokiem, ale miałem dużo praktyki. Kiedy jestem w pracy, rzeczy nie są nigdy takie proste i może nie zawsze wiem, który test wymusi zmianę. Czasami napiszę test i zdziwię się, gdy już minął! Zdecydowanie zalecamy, abyś przed rozpoczęciem miał zwyczaj tworzenia „Listy testowej”. Ta lista testów powinna zawierać wszystkie „interesujące” dane wejściowe, jakie możesz wymyślić. Możesz nie korzystać z nich wszystkich i prawdopodobnie będziesz dodawać przypadki w trakcie podróży, ale ta lista służy jako mapa drogowa. Moja lista testów dla FizzBuzz wyglądałaby mniej więcej tak.
źródło
„Prawdziwy” kod to kod, który piszesz, aby zdać test. Naprawdę . To takie proste.
Kiedy ludzie mówią o pisaniu absolutnego minimum, aby test był zielony, oznacza to po prostu, że twój prawdziwy kod powinien być zgodny z zasadą YAGNI .
Ideą etapu refaktoryzacji jest po prostu wyczyszczenie tego, co napisałeś, gdy będziesz zadowolony, że spełnia on wymagania.
Tak długo, jak pisane testy obejmują wymagania dotyczące produktu, po ich zakończeniu kod jest kompletny. Pomyśl o tym, jeśli wszystkie wymagania biznesowe mają test, a wszystkie z nich są zielone, to co jeszcze można napisać? (Okej, w prawdziwym życiu nie mamy zwykle pełnego zakresu testów, ale teoria jest dobra).
źródło
switch
z przypadkiem dla każdego testu jednostkowego, który przejdzie wszystkie testy i zakończy się niepowodzeniem dla innych danych wejściowych.switch
” miał być logiczną skrajnością „pisania absolutnego minimum, aby testy były zielone”. Widzę pytanie OP jako: gdzie w TDD jest zasada, która unika tak dużegoswitch
?Krótka odpowiedź jest taka, że „prawdziwy kod” to kod, który powoduje, że test jest pozytywny. Jeśli potrafisz zdać test z czegoś innego niż prawdziwy kod, dodaj więcej testów!
Zgadzam się, że wiele samouczków na temat TDD jest uproszczonych. To działa przeciwko nim. Zbyt prosty test dla metody, która, powiedzmy, oblicza 3 + 8, naprawdę nie ma wyboru, musi również obliczyć 3 + 8 i porównać wynik. To sprawia, że wygląda to tak, jakbyś po prostu kopiował cały kod, a testowanie jest bezcelowe, podatne na błędy.
Kiedy jesteś dobry w testowaniu, będzie to informowało o tym, jak tworzysz strukturę aplikacji i jak piszesz kod. Jeśli masz problem ze znalezieniem sensownych, pomocnych testów, prawdopodobnie powinieneś przemyśleć nieco swój projekt. Dobrze zaprojektowany system jest łatwy do przetestowania - co oznacza, że rozsądne testy są łatwe do wymyślenia i wdrożenia.
Kiedy najpierw piszesz testy, obserwuj, jak kończą się niepowodzeniem, a następnie napisz kod, który je zdaje, jest to dyscyplina zapewniająca, że cały kod ma odpowiednie testy. Podczas kodowania nie przestrzegam niewolniczej zasady; często piszę testy po fakcie. Ale najpierw przeprowadzanie testów pomaga zachować uczciwość. Z pewnym doświadczeniem zaczniesz zauważać, kiedy kodujesz się w kącie, nawet jeśli nie piszesz najpierw testów.
źródło
assertEqual(plus(3,8), 11)
, nie byłbyassertEqual(plus(3,8), my_test_implementation_of_addition(3,8))
. W przypadku bardziej skomplikowanych przypadków zawsze szukasz sposobu na udowodnienie poprawności wyniku, innego niż dynamiczne obliczanie poprawnego wyniku w teście i sprawdzanie równości.plus(3,8)
zwrócił prawidłowy wynik, odejmując od niego 3, odejmując 8 od tego i sprawdzając wynik na 0. Jest to oczywiście równoważneassertEqual(plus(3,8), 3+8)
z nieco absurdalne, ale jeśli testowany kod buduje coś bardziej skomplikowanego niż tylko liczba całkowita, wówczas przyjęcie wyniku i sprawdzenie poprawności każdej części jest często właściwym podejściem. Alternatywnie, coś w stylufor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
plus()
może dodać 10 do rzeczy. Oczywiście nadal polegamy na zweryfikowanych przez programistę wartościach pętli początkowych.Czasami niektóre przykłady TDD mogą być mylące. Jak zauważyli wcześniej inni, kod napisany w celu przejścia testów jest prawdziwym kodem.
Ale nie myśl, że prawdziwy kod wygląda jak magia - to źle. Potrzebujesz lepszego zrozumienia tego, co chcesz osiągnąć, a następnie musisz odpowiednio wybrać test, zaczynając od najprostszych skrzynek i skrzynek narożnych.
Na przykład, jeśli chcesz napisać leksykę, zaczynasz od pustego ciągu znaków, potem od kilku białych znaków, potem liczby, potem liczby otoczonej białymi znakami, a następnie niewłaściwej liczby itp. Te małe transformacje doprowadzą cię do właściwy algorytm, ale nie przeskakujesz z najprostszego przypadku do bardzo złożonego przypadku wybranego głupio, aby wykonać prawdziwy kod.
Bob Martin wyjaśnia to tutaj doskonale .
źródło
Część refaktora jest sprzątana, gdy jesteś zmęczony i chcesz wrócić do domu.
Kiedy masz zamiar dodać funkcję, część refaktora zmienia się przed następnym testem. Kod zostanie zmieniony, aby zrobić miejsce dla nowej funkcji. Robisz to, gdy wiesz, jaka będzie ta nowa funkcja. Nie wtedy, gdy to sobie wyobrażasz.
To może być tak proste, jak zmiana nazwy
GreetImpl
, abyGreetWorld
przed utworzeniemGreetMom
klasy (po dodaniu test), aby dodać funkcję, która będzie print „Cześć mamo”.źródło
Ale prawdziwy kod pojawiłby się na etapie refaktoryzacji fazy TDD. Tj. Kod, który powinien być częścią ostatecznej wersji.
Testy powinny być uruchamiane przy każdej zmianie.
Motto cyklu życia TDD brzmiałoby : CZERWONY ZIELONY REFACTOR
CZERWONY : Napisz testy
ZIELONY : Podejmij uczciwą próbę uzyskania kodu funkcjonalnego, który jak najszybciej przejdzie testy: zduplikowany kod, niejasno nazwane zmienne hacki najwyższego rzędu itp.
REFACTOR : Wyczyść kod, poprawnie nazwij zmienne. WYSUSZ kod.
źródło
Czerwony faza jest gdzie napisać kod.
W fazie refaktoryzacji podstawowym celem jest usunięcie kodu.
W fazie czerwonej robisz wszystko, aby test zdał się jak najszybciej i za wszelką cenę . Całkowicie lekceważysz to, co słyszałeś o dobrych praktykach kodowania lub podobnie projektujesz wzór. Ważne jest, aby test był zielony.
W fazie refaktoryzacji usuwasz bałagan, który właśnie stworzyłeś. Teraz najpierw sprawdź, czy właśnie dokonana zmiana jest najwyższa na liście Priorytet transformacji i czy istnieje jakiekolwiek powielanie kodu, które najprawdopodobniej możesz usunąć, stosując wzór projektowy.
Wreszcie poprawiasz czytelność poprzez zmianę nazw identyfikatorów i wyodrębnianie magicznych liczb i / lub literałów z ciągów stałych.
Dzięki za wskazanie na to.
Jest to więc zielona faza, w której piszesz prawdziwy kod
W czerwonej fazie piszesz specyfikację pliku wykonywalnego ...
źródło
Cały czas piszesz Real Code .
Na każdym etapie piszesz kod, aby spełnić warunki, które spełni Twój kod dla przyszłych dzwoniących Twojego kodu (którym możesz być Ty lub nie ...).
Myślisz, że nie piszesz przydatnego ( prawdziwego ) kodu, ponieważ za chwilę możesz go zrestrukturyzować.
Oznacza to, że nawet jeśli zmieniasz kod, warunki, które spełnia kod, pozostają niezmienione. A kontrole ( testy ), które wdrożyłeś, aby zweryfikować Twój kod, już tam są, aby sprawdzić, czy Twoje modyfikacje coś zmieniły. Więc kod, który napisałeś cały czas, jest tam, tylko w inny sposób.
Innym powodem, dla którego możesz myśleć, że to nie jest prawdziwy kod, jest to, że robisz przykłady, w których program końcowy może być już przez Ciebie przewidziany. To jest bardzo dobre, tak jak to widać masz wiedzę o domenie programujesz w.
Ale wiele razy programiści są w domenie , która jest nowy , nieznany im. Nie wiedzą, jaki będzie efekt końcowy, a TDD to technika pisania programów krok po kroku, dokumentująca naszą wiedzę na temat tego, jak ten system powinien działać i sprawdzająca, czy nasz kod działa w ten sposób.
Kiedy czytałem The Book (*) na TDD, najważniejszą cechą, która się wyróżniała, była: lista TODO. Pokazało mi, że TDD jest również techniką pomagającą programistom skupić się na jednej rzeczy na raz. Jest to zatem odpowiedź na Twoje pytanie dotyczące tego, ile prawdziwego kodu napisać ? Powiedziałbym, że wystarczy kodu, aby skupić się na jednej rzeczy na raz.
(*) „Test Driven Development: By Example” autorstwa Kent Beck
źródło
Nie piszesz kodu, aby Twoje testy zakończyły się niepowodzeniem.
Piszesz testy, aby określić, jak powinien wyglądać sukces, który powinien zakończyć się niepowodzeniem, ponieważ nie napisałeś jeszcze kodu, który przejdzie.
Chodzi o to, żeby napisać początkowo nieudane testy, aby zrobić dwie rzeczy:
Chodzi o to, że refaktorem czerwono-zielonym jest to, że napisanie poprawnych testów najpierw daje pewność, że kod napisany do zaliczenia testów jest poprawny, i pozwala refaktoryzować z pewnością, że twoje testy poinformują cię, jak tylko coś się psuje, więc możesz natychmiast wrócić i naprawić.
Z mojego własnego doświadczenia (C # / .NET) czysty test-pierwszy jest trochę nieosiągalnym ideałem, ponieważ nie można skompilować wywołania metody, która jeszcze nie istnieje. Tak więc „najpierw test” dotyczy przede wszystkim kodowania interfejsów i implementacji stubowania, a następnie pisania testów w stosunku do kodów pośredniczących (które początkowo zawiodą), dopóki kody pośredniczące nie zostaną odpowiednio rozwinięte. Nigdy nie piszę „nieudanego kodu”, tylko buduję z kodów pośredniczących.
źródło
Myślę, że możesz się mylić między testami jednostkowymi a testami integracyjnymi. Wierzę, że mogą być również testy akceptacyjne, ale to zależy od twojego procesu.
Po przetestowaniu wszystkich małych „jednostek” przetestujesz je wszystkie złożone lub „zintegrowane”. Zwykle jest to cały program lub biblioteka.
W kodzie, który napisałem, integracja testuje bibliotekę z różnymi programami testowymi, które odczytują dane i przekazują je do biblioteki, a następnie sprawdzają wyniki. Następnie robię to za pomocą wątków. Następnie robię to z wątkami i fork () pośrodku. Następnie uruchamiam go i zabijam -9 po 2 sekundach, następnie uruchamiam go i sprawdzam jego tryb odzyskiwania. Fuzz to. Torturuję to na różne sposoby.
Wszystko to jest TAKŻE testem, ale nie mam ładnego czerwonego / zielonego wyświetlacza dla wyników. Albo się powiedzie, albo przekopię kilka tysięcy wierszy kodu błędu, żeby dowiedzieć się, dlaczego.
To tam testujesz „prawdziwy kod”.
I właśnie o tym pomyślałem, ale może nie wiesz, kiedy należy pisać testy jednostkowe. Skończyłeś pisać testy jednostkowe, gdy testy wykonują wszystko, co podałeś. Czasami możesz zgubić to pośród wszystkich obsługi błędów i przypadków krawędzi, więc możesz chcieć stworzyć przyjemną grupę testową szczęśliwych testów ścieżki, które po prostu przechodzą bezpośrednio przez specyfikacje.
źródło
W odpowiedzi na tytuł pytania: „Kiedy piszesz„ prawdziwy ”kod w TDD?”, Odpowiedź brzmi: „prawie nigdy” lub „bardzo powoli”.
Brzmisz jak student, więc odpowiem tak, jakbyś doradzał uczniowi.
Nauczysz się dużo kodowania „teorii” i „technik”. Świetnie nadają się do spędzania czasu na drogich kursach studenckich, ale przynoszą niewiele korzyści, których nie można przeczytać w książce w połowie czasu.
Zadaniem kodera jest wyłącznie tworzenie kodu. Kod, który działa naprawdę dobrze. Dlatego ty, programista, planujesz kod w umyśle, na papierze, w odpowiedniej aplikacji itp., I planujesz wcześniej obejść możliwe wady / dziury, logicznie i bocznie myśląc przed kodowaniem.
Ale musisz wiedzieć, jak zepsuć aplikację, aby móc zaprojektować porządny kod. Na przykład, jeśli nie wiedziałeś o Little Bobby Table (xkcd 327), prawdopodobnie nie robiłbyś dezynfekcji danych wejściowych przed pracą z bazą danych, więc nie byłbyś w stanie zabezpieczyć swoich danych wokół tej koncepcji.
TDD to tylko przepływ pracy zaprojektowany w celu zminimalizowania błędów w kodzie przez utworzenie testów tego, co może pójść źle przed kodowaniem aplikacji, ponieważ kodowanie może być wykładniczo trudne, im więcej wprowadzasz kodu i zapominasz o błędach, o których kiedyś myślałeś. Gdy pomyślisz, że ukończyłeś aplikację, uruchamiasz testy i boom, mam nadzieję, że błędy zostaną wykryte podczas testów.
TDD nie jest - jak niektórzy sądzą - pisać test, zminimalizować z minimalnym kodem, napisać kolejny test, uzyskać z minimalnym kodem itp. Zamiast tego jest to sposób, aby pomóc ci pewnie kodować. Ten ideał ciągłego refaktoryzowania kodu, aby działał z testami, jest idiotyczny, ale jest to fajna koncepcja wśród studentów, ponieważ sprawia, że czują się świetnie, gdy dodają nową funkcję i wciąż uczą się kodować ...
Proszę, nie wpadnij w tę pułapkę i zobacz swoją rolę kodowania, czym ona jest - zadaniem kodera jest wyłącznie tworzenie kodu. Kod, który działa naprawdę dobrze. Pamiętaj, że będziesz na bieżąco jako profesjonalny programista, a Twojemu klientowi nie będzie zależeć, czy napiszesz 100 000 twierdzeń lub 0. Chcą tylko kodu, który działa. Naprawdę dobrze.
źródło