Kiedy piszesz „prawdziwy” kod w TDD?

147

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.

Jasio
źródło
193
Po TDD ludzie wracają do domu na noc.
hobbs
14
Jak myślisz, dlaczego napisany kod nie jest prawdziwy?
Goyo,
2
@RubberDuck Więcej niż inne odpowiedzi. Jestem pewien, że wkrótce się do tego odniosę. Nadal jest trochę obcy, ale nie zamierzam się z tego poddawać. To, co powiedziałeś, miało sens. Po prostu staram się, aby miało to sens w moim kontekście lub w zwykłej aplikacji biznesowej. Może system ekwipunku lub podobny. Muszę to rozważyć. Jestem jednak wdzięczny za pański czas. Dzięki.
Johnny
1
Odpowiedzi już uderzają w sedno, ale tak długo, jak wszystkie testy przechodzą pomyślnie i nie potrzebujesz żadnych nowych testów / funkcji, można założyć, że kod, który masz, jest skończony, barwienie w pasy.
ESR
3
W pytaniu pojawia się założenie, które może być problematyczne w „Mam dość złożony obiekt o złożonej metodzie”. W TDD najpierw piszesz testy, więc zaczynasz od dość prostego kodu. Zmusi cię to do kodowania przyjaznej dla testów struktury, która będzie musiała być modułowa. Tak złożone zachowanie będzie tworzone przez łączenie prostszych obiektów. Jeśli zakończysz z dość złożonym obiektem lub metodą, wtedy refaktorujesz
Borjab

Odpowiedzi:

243

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.

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.

Assert.Equal("1", FizzBuzz(1));

Łatwy peazy.

public String FizzBuzz(int n) {
    return 1.ToString();
}

Nie to, co nazwałbyś prawdziwym kodem, prawda? Dodajmy test, który wymusza zmianę.

Assert.Equal("2", FizzBuzz(2));

Moglibyśmy zrobić coś głupiego if n == 1, ale przejdziemy do rozsądnego rozwiązania.

public String FizzBuzz(int n) {
    return n.ToString();
}

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?

Assert.Equal("Fizz", FizzBuzz(3));

public String FizzBuzz(int n) {
    if (n == 3)
        return "Fizz";
    return n.ToString();
}

I znowu. Napisz test, który jeszcze nie przejdzie.

Assert.Equal("Fizz", FizzBuzz(6));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    return n.ToString();
}

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.

Assert.Equal("Buzz", FizzBuzz(5));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n == 5)
        return "Buzz"
    return n.ToString();
}

I znowu wiemy, że jest inny przypadek, którym musimy się zająć.

Assert.Equal("Buzz", FizzBuzz(10));

public String FizzBuzz(int n) {
    if (n % 3 == 0)
        return "Fizz";
    if (n % 5 == 0)
        return "Buzz"
    return n.ToString();
}

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.

private bool isDivisibleBy(int divisor, int input) {
    return (input % divisor == 0);
}

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

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.

Assert.Equal("FizzBuzz", FizzBuzz(15));

public String FizzBuzz(int n) {
    if (isDivisibleBy(3, n) && isDivisibleBy(5, n))
        return "FizzBuzz";
    if (isDivisibleBy(3, n))
        return "Fizz";
    if (isDivisibleBy(5, n))
        return "Buzz"
    return n.ToString();
}

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ć.

public String FizzBuzz(int n) {

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

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.

public String FizzBuzz(int n) {

    if (n < 1)
        throw new InvalidArgException("n must be >= 1);

    var isDivisibleBy3 = isDivisibleBy(3, n);
    var isDivisibleBy5 = isDivisibleBy(5, n);

    if ( isDivisibleBy3 && isDivisibleBy5 )
        return "FizzBuzz";
    if ( isDivisibleBy3 )
        return "Fizz";
    if ( isDivisibleBy5 )
        return "Buzz"
    return n.ToString();
}

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.

  • Negatywny
  • Zero
  • Jeden
  • Dwa
  • Trzy
  • Cztery
  • Pięć
  • Sześć (nietrywialna wielokrotność 3)
  • Dziewięć (3 do kwadratu)
  • Dziesięć (nietrywialna wielokrotność 5)
  • 15 (wielokrotność 3 i 5)
  • 30 (nietrywialna wielokrotność 3 i 5)
Gumowa kaczuszka
źródło
3
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
maple_shaft
47
Chyba że całkowicie nie zrozumiem tej odpowiedzi: „Moglibyśmy zrobić coś głupiego, jak gdyby n == 1, ale przejdziemy do rozsądnego rozwiązania”. - wszystko było głupie. Jeśli wiesz z góry, że potrzebujesz funkcji wykonującej <spec>, napisz testy dla <spec> i pomiń część, w której piszesz wersje, które oczywiście nie działają <spec>. Jeśli znajdziesz błąd w <spec>, to na pewno: najpierw napisz test, aby sprawdzić, czy możesz wykonać go przed poprawką, i obserwuj, że test zakończy się poprawką. Ale nie trzeba udawać wszystkich tych pośrednich kroków.
GManNickG
16
Komentarze wskazujące główne wady tej odpowiedzi i ogólnie TDD zostały przeniesione na czat. Jeśli zastanawiasz się nad użyciem TDD, przeczytaj „czat”. Niestety komentarze „jakości” są teraz ukryte wśród wielu rozmów na czacie dla przyszłych studentów.
user3791372,
2
@GManNickG Uważam, że chodzi o to, aby uzyskać odpowiednią liczbę testów. Wcześniejsze napisanie testów ułatwia przeoczenie, które szczególne przypadki powinny zostać przetestowane, co prowadzi albo do sytuacji, które nie są odpowiednio uwzględnione w testach, albo zasadniczo do tej samej sytuacji, która jest bezcelowo objęta wieloma testami. Jeśli możesz to zrobić bez tych pośrednich kroków, świetnie! Jednak nie wszyscy mogą to zrobić, wymaga to praktyki.
hvd
1
A oto cytat z Kenta Becka na temat refaktoryzacji: „Teraz, gdy test się uruchomi, możemy uświadomić sobie (podobnie jak w przypadku„ zrealizowania ”) wdrożenie podsumowania ()”. Następnie przechodzi do zmiany stałej na zmienną. Czułem, że ten cytat całkiem dobrze pasuje do pytania.
Chris Wohlert
46

„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).

GenericJon
źródło
45
Testy jednostkowe nie mogą w rzeczywistości objąć wymagań dotyczących produktu, nawet w przypadku stosunkowo trywialnych wymagań. W najlepszym razie próbkują przestrzeń wejściowo-wyjściową, a ideą jest to, że (poprawnie) uogólniasz do pełnej przestrzeni wejściowej i wyjściowej. Oczywiście, twój kod może być po prostu duży switchz przypadkiem dla każdego testu jednostkowego, który przejdzie wszystkie testy i zakończy się niepowodzeniem dla innych danych wejściowych.
Derek Elkins
8
@DerekElkins TDD nakazuje nieudane testy. Niezaliczone testy jednostkowe.
Taemyr
6
@DerekElkins dlatego nie piszesz tylko testów jednostkowych, a także dlaczego istnieje ogólne założenie, że próbujesz stworzyć coś nie tylko fałszywego!
jonrsharpe
36
@jonrsharpe Zgodnie z tą logiką nigdy nie pisałbym trywialnych implementacji. Np. W przykładzie FizzBuzz w odpowiedzi RubberDuck (która wykorzystuje tylko testy jednostkowe), pierwsza implementacja wyraźnie „po prostu udaje”. Rozumiem to pytanie, dokładnie ta dychotomia między pisaniem kodu, o którym wiesz, że jest niekompletny, a kodem, który naprawdę uważasz, że spełni wymaganie, „prawdziwy kod”. Mój „duży 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żego switch?
Derek Elkins
2
@GenericJon To trochę zbyt optymistyczne z mojego doświadczenia :) Po pierwsze, są ludzie, którzy lubią bezmyślną, powtarzalną pracę. Będą bardziej zadowoleni z gigantycznego komunikatu zamiany niż ze „skomplikowanym podejmowaniem decyzji”. A żeby stracić pracę, albo potrzebują kogoś, kto wzywa ich do tej techniki (i lepiej mieć dobre dowody, że faktycznie tracą szanse / pieniądze firmy!), Albo zrobią wyjątkowo źle. Po przejęciu obsługi wielu takich projektów mogę stwierdzić, że bardzo naiwny kod może trwać przez dziesięciolecia, o ile klient jest zadowolony (i płaci).
Luaan,
14

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.

Carl Raymond
źródło
6
Osobiście test, który napisałbym assertEqual(plus(3,8), 11), nie byłby assertEqual(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.
Steve Jessop
Więc dla naprawdę głupiego sposobu zrobienia tego dla tego przykładu, możesz udowodnić, że 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żne assertEqual(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)
Steve Jessop
... ponieważ pozwala to uniknąć dużego strachu, który polega na tym, że pisząc test popełniamy ten sam błąd na temat „jak dodać 10”, który popełniliśmy w kodzie na żywo. Dlatego test ostrożnie unika pisania kodu, który dodaje 10 do czegokolwiek, w teście, który plus()może dodać 10 do rzeczy. Oczywiście nadal polegamy na zweryfikowanych przez programistę wartościach pętli początkowych.
Steve Jessop,
4
Po prostu chcę zauważyć, że nawet jeśli piszesz testy po fakcie, nadal dobrze jest obserwować, jak zawodzą; znajdź część kodu, która wydaje się kluczowa dla tego, nad czym pracujesz, popraw go nieco (np. zamień + na - lub cokolwiek), uruchom testy i zobacz, jak się nie udają, cofnij zmianę i zobacz, jak przechodzą. Wiele razy to robiłem, test faktycznie nie zawiódł, co czyni go gorszym niż bezużytecznym: nie tylko nie testuje niczego, ale daje mi fałszywą pewność, że coś jest testowane!
Warbo
6

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 .

Victor Cejudo
źródło
5

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, aby GreetWorldprzed utworzeniem GreetMomklasy (po dodaniu test), aby dodać funkcję, która będzie print „Cześć mamo”.

candied_orange
źródło
1

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.

graeme
źródło
6
Wiem, co mówisz o fazie „zielonej”, ale to sugeruje, że wartości zwrotne dla twardego połączenia, aby pomyślnie przejść testy, mogą być odpowiednie. Z mojego doświadczenia wynika, że ​​„zielony” powinien być uczciwą próbą sprawienia, aby działający kod spełniał wymagania, może nie być idealny, ale powinien być tak kompletny i „nadający się do wysyłki”, jak programista może zarządzać w pierwszym przejściu. Refaktoryzacja najlepiej jest zrobić trochę później, po tym jak dokonasz więcej prac rozwojowych, a problemy z pierwszym przejściem staną się bardziej widoczne i pojawią się możliwości DRY.
mcottle,
2
@mcottle: możesz być zaskoczony, ile implementacji repozytorium „tylko do odczytu” może być zakodowanymi wartościami w bazie kodu. :)
Bryan Boettcher,
6
Dlaczego miałbym kiedykolwiek pisać bzdury i je wyczyścić, skoro mogę wypisać ładny, jakościowy kod prawie tak szybko, jak potrafię pisać? :)
Kaz
1
@Kaz Ponieważ w ten sposób ryzykujesz dodanie zachowania niesprawdzonego . Jedynym sposobem na zapewnienie testu dla każdego pożądanego zachowania jest dokonanie możliwych możliwych zmian bez względu na to, jak okropne to jest. Czasami następujące refaktoryzacja wprowadza nowe podejście, o którym wcześniej nie pomyślałeś ...
Timothy Truckle
1
@TimothyTruckle Co to jest, jeśli znalezienie najprostszej możliwej zmiany zajmuje 50 minut, ale tylko 5, aby znaleźć drugą najprostszą możliwą zmianę? Czy idziemy z drugą najprostszą czy szukamy najprostszej?
Kaz
1

Kiedy piszesz „prawdziwy” kod w TDD?

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.


To nie jest czerwony refaktor, to jest czerwony-zielony refaktor. - Rob Kinyon

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 ...

Timothy Truckle
źródło
To nie jest czerwony refaktor, to jest czerwony-zielony refaktor. „Czerwony” oznacza, że ​​zmieniasz zestaw testów z zielonego (wszystkie testy zdają) na czerwony (jeden test kończy się niepowodzeniem). „Zielony” to miejsce, w którym niechętnie przenosisz zestaw testów z czerwonego (jeden test się nie udaje) do zielonego (wszystkie testy kończą się pomyślnie). „Refaktor” to miejsce, w którym bierzesz swój kod i upiększasz go, zachowując wszystkie testy.
Rob Kinyon
1

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ć.

Refaktoryzacja kodu to proces restrukturyzacji istniejącego kodu komputerowego - zmiana faktoringu - bez zmiany jego zachowania zewnętrznego.

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

Robert Andrzejuk
źródło
2
„Test Driven Development: by example” Kent Beck
Robert Andrzejuk
1

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:

  1. Obejmuje wszystkie skrzynki - wszystkie skrzynki nominalne, wszystkie skrzynki brzegowe itp.
  2. Zatwierdź swoje testy. Jeśli tylko zobaczysz, jak przemijają, to skąd możesz mieć pewność, że rzetelnie zgłoszą awarię, gdy taka nastąpi?

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.

Zenilogix
źródło
0

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.

Zan Lynx
źródło
(jego = dzierżawczy, to = "to jest" lub "ma". Zobacz na przykład: Jak korzystać z jego i jej .)
Peter Mortensen
-6

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.

użytkownik3791372
źródło
3
Nie jestem nawet blisko ucznia, ale czytam i staram się stosować dobre techniki i być profesjonalistą. W tym sensie jestem „uczniem”. Po prostu zadaję bardzo podstawowe pytania, bo taki jestem. Lubię dokładnie wiedzieć, dlaczego robię to, co robię. Sedno sprawy. Jeśli tego nie rozumiem, nie podoba mi się to i zaczynam zadawać pytania. Muszę wiedzieć dlaczego, jeśli mam z niego korzystać. TDD wydaje się intuicyjnie dobry pod niektórymi względami, np. Wiedząc, co trzeba stworzyć i przemyślając różne rzeczy, ale implementacja była trudna do zrozumienia. Myślę, że teraz lepiej rozumiem.
Johnny
4
Takie są zasady TDD. Możesz dowolnie pisać kod, ale jeśli nie będziesz przestrzegać tych trzech zasad, nie będziesz stosować TDD.
Sean Burton
2
„Zasady” jednej osoby? TDD jest sugestią, aby pomóc ci w kodowaniu, a nie religią. To smutne, że tak wielu ludzi tak anonimowo stosuje się do pomysłu. Nawet pochodzenie TDD jest kontrowersyjne.
user3791372,
2
@ user3791372 TDD jest bardzo ścisłym i jasno zdefiniowanym procesem. Nawet jeśli wielu uważa, że ​​to oznacza tylko „Wykonaj testowanie podczas programowania”, tak nie jest. Spróbujmy nie mieszać tutaj warunków, to pytanie dotyczy procesu TDD, a nie ogólnych testów.
Alex