Dlaczego zwinność polega na testowaniu testowym (TDD), a nie testowym (DDT)?

74

Więc jestem nowy w zwinnym, ale nie testowym rozwoju . Moi profesorowie na studiach opowiadali o testach, potem o kodzie, a potem o testach. Nie jestem pewien, czy rozumiem dlaczego. Z mojego punktu widzenia jest to duży koszt początkowy, który najprawdopodobniej zostanie zmieniony w miarę ewolucji twojego kodu.

Tak wyobrażam sobie TDD i dlaczego mnie to myli. Gdybym miał zbudować dom jako wykonawca TDD.

  1. Podaj mi wszystkie swoje specyfikacje (historie).

  2. Uzyskaj zatwierdzenie specyfikacji.

  3. Rozbij wszystkie specyfikacje do inspekcji, które moim zdaniem będą potrzebne (patrz w przyszłość).

  4. Zadzwoń do inspektora, aby przejrzał te punkty i powiedz mi teraz, że nie udaje mi się inspekcja (rany, dzięki).

  5. Zacznij budować dom.

  6. Codziennie wzywaj inspektora (przekazując 2/100).

  7. Och, strzel, był problem z moim zrozumieniem i teraz muszę dodać jeszcze 9 inspekcji i zmienić 27 z nich.

  8. Zadzwoń do inspektora z wynikiem 1/109.

  9. Cholera. Dlaczego inspektor nie lubi tego ... och zaktualizowałem nazwę tej metody ...

  10. Zbuduj trochę więcej.

  11. UGGGGHHHH WIĘCEJ ZMIAN Pozwól mi zaktualizować cholernego inspektora. Och, nie udaje mi się nie **

  12. Czy już skończyłem?

Okej, to może być dziwaczne, ale po prostu nie widzę, jak powinienem znać wszystkie moje metody i jak będą działać, dopóki nie będzie mojego kodu. 99% czasu muszę wracać i aktualizować test jednostkowy w dowolny sposób i dodawać kolejne. Wydaje się po prostu wstecz.

Wydaje się, że bardziej odpowiednie są testy DDT lub testy rozwojowe, o których społeczność prawie zapomniała.

Z mojego zrozumienia DDT dla domu wyglądałby następująco:

  1. Podaj mi wszystkie swoje specyfikacje (historie).

  2. Uzyskaj specyfikację i przełam ją.

  3. Uruchom jednostkę (fundament).

  4. Rób notatki (komentarze) na temat podstępnej logiki.

  5. Na koniec przed rozpoczęciem kolejna jednostka ma kontrolę (utwórz test).

  6. Napraw wszelkie znalezione problemy i sprawdź ponownie.

  7. Zatwierdzona ta jednostka przechodzi do następnej.

Jeśli wszyscy jesteśmy szczerzy, czy to nie brzmi bardziej ludzko i koncentruje się na deweloperze i biznesie? Wygląda na to, że zmiany można wprowadzać szybciej i bez nakładania się TDD.

nerdysta
źródło
60
Rażące rozłączenie jest dla mnie pomysłem, że możesz mieć ponad 100 nieudanych testów przed napisaniem kodu, który je przechodzi. Jak mówi @Telastyn, nie powinno być więcej niż kilka nieudanych testów na raz. „Testuj, a potem kod, a następnie test” nie oznacza napisania wszystkich testów aplikacji przed napisaniem jakiegokolwiek kodu.
Eric King,
33
Metafory są antypatternem! Ludzie spędzą więcej czasu na wyjaśnianiu, dlaczego twoja metafora nie pasuje do tematu, niż na rozwiązywaniu twoich obaw. W tym przypadku wydaje się, że naprawdę martwisz się, że musisz napisać wszystkie testy z góry.
JacquesB
21
Na marginesie, TDD jest powszechnie stosowany w zwinnych projektach, ale myślę, że ważne jest, aby odróżnić zwinne zarządzanie projektami od zwinnych praktyk programistycznych. Pomiędzy nimi nie ma bardzo silnego związku. Możesz korzystać z najbardziej zwinnych praktyk programistycznych, niezależnie od metodologii projektu i odwrotnie. Często zapomina się, że zwinność, tak naprawdę, polega na dostosowaniu podejścia do potrzeb, aby osiągnąć najlepszy wynik, a nie na nakazowej liście rzeczy, które musisz zrobić. Innymi słowy, Agile! = Scrum + TDD.
JimmyJames
35
Dla przypomnienia, porównywanie rozwoju oprogramowania do budowy domu jest bezcelowe. Badania zostały wykonane. Wyniki są. Odpowiedź jest jasna. Po prostu nie budujesz oprogramowania w taki sam sposób, jak budujesz dom. The. Dwa. Są Różne.
riwalk
14
Zwinność polega na szybkich pętlach sprzężenia zwrotnego, TDD jest tylko jednym szczególnym rodzajem pętli sprzężenia zwrotnego
jk.

Odpowiedzi:

83

Jedną z zalet podejścia TDD jest realizacja tylko wtedy, gdy wykonujesz również nowe projekty.

Zatem w pierwszej analogii nie napisałbyś 100 testów, ponieważ nie ma możliwości, abyś wiedział, jak będzie wyglądało twoje oprogramowanie.

Piszesz jeden test. Uruchom to. To nie wyszło. Pisz najmniejszą jednostkę kodu, aby pomyślnie przejść test. Następnie ponownie uruchom test. To mija.

Teraz napisz kolejny test, powtarzając powyższy proces.

To może wydawać się marnotrawstwem na samym początku, kiedy jest oczywiste, co powinien zrobić twój kod, ale wielką zaletą tego podejścia jest to, że zasięg testu jest zawsze wysoki, a projekt kodu jest w ten sposób czystszy.

Jako metoda idzie w parze z programowaniem par; jedna para pisze test, następna zapisuje kod, aby go przejść, a następnie zapisuje następny test i tak dalej.

Korzystam z tego podejścia nawet podczas pisania nowej klasy; pierwszy test to wywołanie inicjujące konstruktor klasy. Ale nie napisałem jeszcze zajęć, więc to się nie udaje. Następnie piszę prostą, pustą klasę i mój pierwszy test kończy się pomyślnie.

Po przejściu do sposobu myślenia bardzo trudno jest nie być w nim i kodować „staromodnym” sposobem.

Poleciłbym pracę w dobrym środowisku Agile, aby się tego nauczyć, lub przeczytanie kilku dobrych książek Agile (zarówno Clean Code, jak i Clean Coder są bardzo dobre), aby lepiej zrozumieć.

Phil Riley
źródło
46
Termin „Emergent Design” brzmi, jakbyś mógł rozwinąć projekt oprogramowania, wykonując testy. Nie możesz
Robert Harvey
28
@nerdlyist: Nie zapomnij jednak o designie. Nadal musisz zaprojektować oprogramowanie; dobry projekt nie tylko naturalnie wyłania się z reaktora czerwono-zielonego. TDD zachęca do dobrego projektu, ale go nie tworzy.
Robert Harvey
19
@RobertHarvey, całkowicie nie zgadzam się z twoim stwierdzeniem „ Termin„ Emergent Design ”brzmi, jakbyś mógł rozwinąć projekt oprogramowania, wykonując testy. Nie możesz ”. Robiąc to wielokrotnie, zapewniam cię, że da się to zrobić.
David Arno,
12
@DavidArno: Czy zaczynasz od przygotowania specyfikacji wymagań? Jak przełożyć tę specyfikację na rozsądną architekturę? Czy za każdym razem wymyślasz to na nowo za pomocą testów, czy masz na myśli wcześniejsze zasady architektury? Nikt nie robi tego na ślepo; w pisaniu testów nie ma nic magicznego, poza tym, że zmusza cię to do uczynienia kodu bardziej testowalnym.
Robert Harvey
45
@DavidArno Myślę, że twój sprzeciw wynika z niedostatecznego uznania się za doświadczonego programistę / architekta. Ponieważ już wiesz, jak wygląda moduł X intuicyjnie , twoje testy poprowadzą twój rozwój w dobrym kierunku. Niedoświadczony programista, który nie ma intuicyjnego pojęcia, jak powinien wyglądać moduł przed napisaniem testów, zbuduje dobrze przetestowany, gówniany moduł.
svidgen
86

Oprogramowanie to nie dom. Intuicja jest dobra, ale zrozum, że nie zawsze jest poprawna.

Rozbij wszystkie specyfikacje do inspekcji, które moim zdaniem będą potrzebne (patrz w przyszłość).

To nie jest dokładne. W TDD opisujesz, jak chcesz użyć kodu. Specyfikacje mówią „Musi być dom ze sposobem, aby do niego wejść”. Test mówi wtedy: „Hej, chcę mieć drzwi frontowe z gałką”. Jest to o wiele mniej patrzenie w przyszłość niż rozpoczęcie od budowy ościeżnicy, klamki, zamka itp. (A przynajmniej tak argumentuje).

Codziennie wzywaj inspektora (przekazując 2/100).

To nie jest poprawne Nie piszesz testów dla domu. Piszesz testy dla framugi drzwi, a potem robisz je zielone. Następnie sprawdź, czy drzwi są solidne, co czyni je zielonymi. W danym momencie powinieneś mieć najwyżej kilkanaście testów. Zwykle jest bliżej dwóch do czterech.

Analogia „wezwać inspektora” oznacza, że ​​potrzeba czasu, aby osoba wyszła i wykonała swoją pracę. Uruchomienie testów jednostkowych dla iteracji TDD powinno zająć dosłownie kilka sekund.

Wygląda na to, że zmiany można wprowadzać szybciej i bez nakładania się TDD.

Koszty ogólne są mniejsze, niż się wydaje. Kilka sekund na uruchomienie testów, może pół tuzina razy, jest nieistotne dla ogólnego czasu programowania.

Problem deweloperów polega na tym, że czasami, kiedy przechodzisz do testowania, okazuje się, że jest duży problem. Tak, jakbyś położył łóżko obok toalety i nikt nie chce tam mieszkać. Rzeczy, które wymagają naprawy dłużej niż jakikolwiek narzut TDD. Rzeczy, które złapałby TDD, ponieważ TDD zmusza cię do użycia kodu od samego początku i zastanowienia się, jak się z nim połączyć.

Moi profesorowie na studiach opowiadali o testach, a potem o kodzie, a potem o teście.

Wszystko, co powiedziało, TDD nie jest tak wszechobecne. Wiele miejsc nadal zajmuje się tworzeniem oprogramowania, a wiele rzekomych korzyści z TDD jest przesadzonych. Ważne jest, aby wykonać testy i uczynić je dobrymi. Kolejność wykonywania pracy jest mniej ważna.

Telastyn
źródło
18
+1 tylko za ostatni akapit. Wasze przykłady domów naprawdę podkreślają, jak głupie może być „religijne TDD”.
Robert Harvey
23
„Ważne jest, aby wykonać testy i uczynić je dobrymi. Kolejność wykonywania pracy jest mniej ważna. ” Jestem wielkim fanem Test-Driven-Development (TDD) i często się za tym opowiadam. Jednak pisanie kodu jest bałagan. W przypadku trudniejszych, mniej zdefiniowanych problemów zwykle muszę najpierw napisać kod, zanim zrozumiem, jakie są wymagania. Następnie mogę napisać wymagania, a następnie napisać testy.
Trevor Boyd Smith,
1
@TrevorBoydSmith nie pisałbyś w tym momencie dokumentacji, a nie wymagań.
nerdlyist
8
Ponieważ TDD stawia użytkownika kodu z perspektywy , [w najlepszym przypadku] promuje lepsze interfejsy. Na przykład, kiedy dzwonię, open_doornie powinienem przekazywać flagi toilet_flushed; więc nie piszę mojego testu w ten sposób, dlatego mój kod nie ma takich „szorstkich krawędzi”. Kod napisany całkowicie bez wystarczającego uwzględnienia tego, jak się go nazywa, często ma dziwne interfejsy lub założenia / warunki wstępne.
brian_o
3
@TrevorBoydSmith, aby nadać mu nazwę, słyszałem, że nazywa się „Spike & Stabilize” i tak, jest to przydatna technika podczas odkrywania, jak zrobić coś, czego jeszcze nie wiesz.
RubberDuck
13

Podobieństwa między budowaniem rzeczy fizycznych a oprogramowaniem do pisania są dość minimalne.

To powiedziawszy, warto podkreślić jedno ogromne rozróżnienie:

Istnieje różnica między „tworzeniem testu” a „wykonywaniem testu”.

W przykładzie budowy domu, wymagania i badania mają poprzedzać fizyczną buildout. Części pakietu testowego są uruchamiane w sposób ciągły - nawet przez wielu agentów! Gdy konstruktor podnosi 2x4, natychmiast i automatycznie „testuje” jednostkę pod kątem jego pojęcia „jak wygląda dźwięk 2x4”. Nie pisze wymagań po tym, jak je podniósł; przeprowadza na nim wcześniejsze kontrole.

Podobnie w przypadku zmontowanej ściany, skrzynki elektrycznej, hydrauliki itp. - testy / wymagania już istnieją; biegną one domyślnie i automatycznie przez wyszkolone oczy wszystkich osób w pracy w sposób ciągły . Kolejny zestaw wcześniej istniejących testów jest wykonywany podczas wizyty inspektora. Jeśli jednostki nie zostały zbudowane tak, aby przejść te istniejące testy, nie powiedzie się.

Podobnie jest w przypadku całej konstrukcji. Plany istnieją wcześniej. Na każdym etapie inżynierowie pracują nad stworzeniem istniejącego zestawu warunków, na podstawie których konstrukcja zostanie ostatecznie przetestowana po zakończeniu budowy.

To powiedziawszy, nie każdy fizyczny projekt musi być poprzedzony ogromnym zestawem testów. Jeśli pójdziesz do warsztatu i zaczniesz składać drewno na skrzynkę z zabawkami lub coś takiego, pozwalając, by kierowała Cię intuicja i kreatywność, nie jest to rygorystyczna obróbka drewna TDD. W takim przypadku polegasz w zasadzie na prawach fizycznych medium i swoich surowych oczekiwaniach dotyczących walidacji dzieła (tj. „Jeśli się kompiluje i działa, to dobrze!”).

I to jest w porządku. Nie wszystko musi być poprzedzone rygorystycznymi testami.

tl; dr

Mimo że konstrukcja! = Oprogramowanie do pisania: konstrukcja działa w sposób testowy. Warunki „przejście” dla każdej jednostki zrobić poprzedzać ich buildout.

Nie należy łączyć „wykonywania testów” z „pisaniem testów”.

Nie wszystko musi być TDD.

svidgen
źródło
Dostaję kod! = Fizyczny to analogia. Ale to faktycznie wyjaśnia mi trochę więcej. Jak wskazał mój inny komentarz, zastanawiam się, czy najpierw stworzy to test, który powstrzymuje chęć powrotu i wprowadzenia większych zmian?
nerdlyist
@nerdlyist W pewnym sensie tak. Ale jeśli twoje testy sprawdzają „właściwe” rzeczy, to chcesz „przeszkadzać” .
svidgen
4
Wracając do analogii domu: jeśli Twój klient nagle chce zamienić drzwi na inny styl i nie ma takich samych wymiarów, wymagania dotyczące drzwi i wymiarów otworów się zmienią, a praca wymaga zmiany tych „testów” ”powinien odzwierciedlać pracę wymaganą do zmiany wdrożenia. Ale jest wiele testów, które się nie zmienią: drzwi muszą się otwierać i zamykać, zamykać, i być szczelne P% w otworze po zamknięciu itp. Wiele testów, które nie zmieniają tego „utrudniania” zmiany fizyczne odpowiednio .
svidgen
1
Gdybym mógł dać +1 jeszcze raz, chciałbym rozważyć ten ostatni kawałek. Nie wszystko wymaga testu. Ponowne przeczytanie go sprawiło, że pomyślałem, że może łączę testy jednostek TDD z testami integracji i regresji. Pracownicy automatycznie testują jednostki, które inspektor integruje, a testy regresji są wykonywane przy wprowadzaniu większych zmian.
nerdlyist
1
@nerdlyist Integracja i testy regresji można wykonać również w pierwszej kolejności. Takie postępowanie nie do końca pasuje do cyklu rozwojowego „dogmatyków TDD”, ale wynik jest w dużej mierze taki sam: kończysz na testowalnym kodzie, z minimalnym kodem zewnętrznym i testami, które faktycznie potwierdzają to, co twierdzą. Testowanie po napisaniu kodu może sprawić, że wykonanie jednej lub wszystkich tych rzeczy będzie nieco trudniejsze. Wcale nie niemożliwe, ale potencjalnie trudniejsze.
svidgen
11

Wpadłeś w pułapkę wiary w nonsensowny pomysł, że pisanie oprogramowania jest analogiczne do budowy domu. To nie jest Jest to bardziej analogiczne do tworzenia rysunków architektów i obliczeń inżynierów budownictwa.

Teraz z prawdziwymi domami architekt tworzy te plany z góry. Następnie przywołujesz budowniczych, którzy zaczynają budować, napotykają problemy, muszą coś zmieniać, uzyskać kontrolę nad budynkiem, którzy chcą zmian itp. Na koniec architekt wraca i pobiera opłaty za aktualizację swoich rysunków, aby odzwierciedlić co się stało. Jest to kiepski sposób robienia rzeczy, ale budowanie domów zajmuje dużo czasu i jest drogie, więc jest to jedyny praktyczny sposób.

W inżynierii oprogramowania jest lepiej. Odpowiednikiem budynku w domu jest to, że kompilator zamienia kod w jakąś skompilowaną jednostkę (biblioteka, aplikacja, aplikacja internetowa itp.). Robienie tego setki razy dziennie jest bardzo szybkie i tanie. W związku z tym wielokrotne przebudowywanie domu nie ma sensu podczas dodawania funkcji, a na koniec należy tylko wezwać inspektora (QA), aby go przetestować. Zamiast tego, jeśli zautomatyzujesz te kontrole, możesz poprosić inspektora o ponowne sprawdzenie wszystkiego przy każdej przebudowie.

Niezależnie od tego, czy przestrzegasz ścisłego TDD, czy bardziej zorientowanego na testy podejścia do pisania kodu, to niektóre testy nie mają znaczenia. Wolę pierwsze podejście, inne to drugie. Wybierz ten, który Ci odpowiada. Ważną rzeczą jest to, że tworzysz te czeki na bieżąco. Pomagają później, gdy chcesz zmienić kod, ostrzegając cię o przerwach w funkcjonowaniu w innym miejscu, gdy coś zmienisz.

David Arno
źródło
1
Widzę, że miałeś wtedy do czynienia z Kontrolą budynku ... :)
Jules
5
„Jest to kiepski sposób robienia rzeczy, ale budowanie domów zajmuje dużo czasu i są drogie, więc jest to jedyny praktyczny sposób.”: Prawdą jest, że w oprogramowaniu można bardzo szybko odbudować, ale jeśli popełnisz błąd decyzja na początku może później wymagać dużego przepisania. Przyrostowy projekt nie zawsze łapie tego rodzaju problemy: poruszasz się małymi krokami, aż znajdziesz się w ślepej uliczce i musisz wrócić. Nie ma srebrnej kuli.
Giorgio
2
@Giorgio - zdarzają się duże zmiany, ponieważ pojedyncze decyzje znajdują odzwierciedlenie w wielu miejscach kodu. Kiedy decyzja się zmienia, kod zmienia się w wielu miejscach. Ciągłe refaktoryzowanie zmniejsza ten problem, eliminując powielanie i ograniczając ilość kodu odzwierciedlającą pojedynczą decyzję. Dobry zasięg testu wspiera ciągłe refaktoryzowanie.
kevin cline,
7

Przede wszystkim koszt początkowy nie jest tak wysoki, jak myślisz . Tak, poświęcasz więcej czasu na rozwiązywanie problemów związanych z testowaniem, niż w przypadku braku testów. Ale jeśli wykonasz metodę „test po”, co tak naprawdę marnujesz? Powiedzmy, że TDD zajmuje 10 godzin, a DDT 6 godzin. Twoje „dodatkowe” koszty wstępne wynoszą tylko 4 godziny. Teraz, jeśli zastosujesz metrykę kodu lub wymaganie, takie jak pokrycie 90%, wówczas TDD i DDT będą jeszcze bardziej kosztowne.

Dzięki TDD napiszesz mniej wadliwego kodu. Nawet jeśli dzieje się tak tylko dlatego, że określiłeś wymagania jako test, na koniec dnia możesz udowodnić, że Twój kod działa dokładnie tak, jak chciałeś. Być może chciałeś, aby zrobiła to źle, ale to nie jest błąd, ale prośba o zmianę. To jest ważne. Łatwiej jest „sprzedać” produkt, który działa, ale mógłby działać inaczej / lepiej, niż sprzedawać produkt, który jest postrzegany jako niedziałający. Z TDD dosłownie niemożliwe jest zdanie testu i napisanie niedziałającego kodu. Możliwe, że nie zrozumiałeś wymagań i napisałeś zły, ale działający kod.

TDD jest tym lepsze, im starsza jest podstawa kodu. Spróbuj refaktoryzować bez pakietu testowego lub ze słabo zaimplementowanym. Nawet prosta zmiana może wprowadzić błędy. Posiadanie zestawu testów z dobrym zasięgiem gwarantuje, że wraz z rozwojem produktu będzie on nadal działał tak, jak powinien. Pomaga także uwypuklić sprzeczne reguły biznesowe (które zdarzają się przez dłuższy czas).

Nie wiesz, że to nie działa. Bez pakietu testowego nie wiesz, czy Twój kod działa tak, jak myślisz, czy po prostu działa.

var foo = function(in) {
    if(in == 0) {
      return true
    }
}

Teraz w całej aplikacji dzwonisz if(foo()){ doStuff() }Co się stanie, gdy naprawię foo?

var foo = function(in) {
    if(in === 0) {
      return true
    }
}

Powinieneś także refaktoryzować i suszyć swoje testy. Dobry zestaw testów nie jest trudny w utrzymaniu. Dzięki dobrze napisanym testom atomowym rzadko musiałem wracać i zmieniać więcej niż 1-2 na raz. Kiedy nastąpiły większe zmiany w pakiecie testowym, jest to wielka czerwona flaga, że ​​coś jest nie tak.

Po prostu nie widzę, jak powinienem znać wszystkie moje metody i jak będą działać, dopóki nie będzie mojego kodu.

Cóż, nie powinieneś. Powinieneś napisać test, który sprawdza, czy wykonano pewną część pracy. Jeśli czujesz, że testujesz rzeczy, o których prawdopodobnie nie możesz wiedzieć, to myślisz, że jest za duży, LUB testowanie jest za małe.

Na przykład musisz wiedzieć, że drzwi się zamykają i blokują. Testowałbym door.close () i door.lock (), a door.open () zwraca wartość false, gdy drzwi są zamknięte. To jest to. Jeśli twoje testy to door.lock () ustawia flagę w bazie danych. Więc testujesz za mały. Test nie musi wiedzieć, jak działa door.lock (), tylko że działa.

Teraz, jeśli piszesz test, który mówi, że house.isSecure () zwraca true, gdy wszystkie drzwi i okna są zamknięte. Nie patrząc najpierw na drzwi lub okna, myślisz, że jest za duży.

Wreszcie możesz patrzeć na zbyt dużą jednostkę pracy . Po otrzymaniu listy wymagań należy je uporządkować, aby pracować na najmniejszym urządzeniu. Napisz test tylko dla tej jednostki, następnie kod, a następnie spłucz i powtórz.

Zasadniczo twoje zrozumienie (lista) tego, jak powinien działać TDD, jest błędne. Nigdy nie powinieneś mieć 2/100 podania. Powinieneś mieć 1/1 podania, następnie 2/2 podania, następnie 3/3 podania, następnie 4/4 podania i tak dalej.

Zmieniona lista dla Ciebie

  1. Uzyskaj wszystkie specyfikacje
  2. Wybierz jedną specyfikację
  3. Sprawdź to
  4. Kod to
  5. Sprawdź to
  6. Jeśli testy przejdą, przejdź do 7, przejdź do 4
  7. Jeśli wykonałeś wszystkie specyfikacje, przejdź do 8, przejdź do 2
  8. Przejrzyj specyfikacje z konsumentem i dodaj nowe specyfikacje w razie potrzeby. Jeśli zrobisz to poprawnie, nie powinieneś zmieniać żadnych testów. Po prostu dodaj nowe. Czasami zbieranie wymagań może się rozpaść i trzeba dodać testy, które są sprzeczne z wcześniejszymi testami, ale rzadko trzeba zmieniać testy.
Coteyr
źródło
2
Podoba mi się ta odpowiedź, ponieważ podkreśla ona prawdziwe zalety TDD - sprawia, że ​​starsze bazy kodów są łatwe do zarządzania. Wiele osób, które pracowały tylko nad fazą projektu od podstaw, nie widzi korzyści z TDD, ponieważ wygląda na to, że to tylko dodatkowa praca. To naprawdę się opłaca, gdy musisz zaktualizować i utrzymać istniejącą bazę kodów. Chciałbym, aby w szkole dali ci projekt, a gdy tylko go uruchomisz, poproś o kilka zmian funkcji. To bardziej odzwierciedlałoby rzeczywistość i pokazywało wartość TDD.
thomij
1
Podoba mi się również ta odpowiedź. Zauważ, że zwykle zapominamy, ile czasu faktycznie spędzamy na ręcznym wykonywaniu testów: edytuj, kompiluj, przechodź do ręcznego debugowania, powtarzaj. TDD automatyzuje wiele z tego.
Technophile
To, co mnie wyskakuje, polega na tym, że ważne jest, aby pamiętać, że kiedy piszesz test jednostkowy w TDD, nie piszesz testu na część specyfikacji funkcjonalnej klienta, piszesz test na zachowanie jednostka, którą Ty - programista - zdefiniowałeś w swoim umyśle, tzn. „ta klasa powinna wywołać zdarzenie, gdy X się zdarzy, napiszmy na to test”. To niejasne „twoje testy są twoją specyfikacją!” retoryka jest świetna, gdy już masz TDD, ale dla kogoś z pętli rodzi więcej pytań niż odpowiedzi.
Ant P
4

Są pewne klucze, które, jak sądzę, brakuje innych odpowiedzi.

Trzy zalety

Po pierwsze, koszt błędu i koszt zmiany są tak niewiarygodnie różne w przypadku oprogramowania i domu, że niektóre zasady mogą nie mieć zastosowania. Na przykład koszt testowania struktury fizycznej jest tak wysoki, że potrzebujesz wysokiego stopnia pewności jej działania, aby nie zmarnować testowania. Dzięki oprogramowaniu, jeśli możesz przeprowadzić pakiet testów jednostkowych w ciągu 1–5 sekund, mamy do dyspozycji różne opcje.

Po drugie, celem wykonania testu, który powinien zakończyć się niepowodzeniem przed napisaniem testowanego kodu, jest sprawdzenie samego testu. Pewnie to może wydawać się głupie lub marnotrawstwem. Ale widziałem wystarczającą liczbę testów jednostkowych napisanych w sposób, który zawsze przejdzie, nawet gdy testowany kod jest zepsuty. Może się to łatwo zdarzyć, gdy napiszesz testowany kod, przetestuj go ręcznie, a następnie napisz test jednostkowy. Jeśli napiszesz test jednostkowy, zobacz, że się nie powiedzie, a następnie, gdy napiszesz kod potrzebny do jego zaliczenia, a on przejdzie, wiesz, że test jest poprawny. Jeśli testowany kod zostanie zresetowany, istnieje uzasadniona szansa, że ​​zostanie wykryty przez test jednostkowy.

Trzecią zaletą TDD, o której często nie wspomina się, jest to, że wynikowy rozmiar i złożoność kodu jest często o rząd wielkości mniejsza. Zawsze byłem dumny z tego, że wolę prosty i zwięzły kod. Aż zacząłem ćwiczyć TDD. TDD pokazało mi, że to, co zrobiłbym wcześniej, byłoby nadmiernym kodem. Dlaczego to się dzieje? Ponieważ piszesz test, a następnie napisz kod, aby test przeszedł pomyślnie. Po przejściu testu skończymy. Jeśli jesteś w tym umyśle, trudno jest przypadkowo napisać „dodatkowy” kod.

(Dodatkowy kod był problemem, który zaobserwowałem w produkcie, nad którym kiedyś pracowałem. Poważne problemy wynikające z kodu, o który nikt nie prosił, ale niektórzy programiści sądzili, że będzie fajny.)

Analiza kosztów

Pod pewnymi względami masz rację. Nie udało nam się uniknąć tej strategii przy budowie domu. Byłoby to zbyt drogie. Ale oprogramowanie to nie dom. Oprogramowanie jest tanie.

Analogią do domu jest praca nad zestawieniem domu w porównaniu z kompilatorem oprogramowania.

W świecie innym niż TDD programiści wciąż iterują. Postępujemy zgodnie z kodem -> kompiluj -> uruchom -> cykl testowy. Jesteśmy już w modelu, który znacznie odbiega od budowy domu. Jeśli twoi konstruktorzy zbudują ościeżnicę, a następnie zbuduj drzwi, a następnie przebuduj ościeżnicę, ponieważ drzwi są na nią zbyt duże, będziesz miał problem z kosztami. Dlatego spędzasz więcej czasu z góry, aby upewnić się, że wszystko jest idealne. W programowaniu większość projektów można skompilować w kilka sekund lub minut, więc nie ma znaczenia, czy coś jest niedoskonałe na początku. Koszt myślenia o sprawach trywialnych z wyprzedzeniem zwykle przewyższa koszt ponownej kompilacji. Tak więc cały czas rekompilujemy.

TDD to ta sama zasada, tylko obrócona, więc test idzie do przodu. Jeśli testowanie może być super tanie (aby wszystko trwało kilka sekund), wówczas koszt przemyślenia dużego obrazu, idealnego, ogólnego rozwiązania w pojedynczej iteracji kodowania Big Bang przewyższa koszt refaktoryzacji.

Z wyjątkiem...

W programowaniu są pewne rzeczy, w których te argumenty się nie utrzymują. Taki jest cel architektury. Zidentyfikuj i zaplanuj z wyprzedzeniem obawy, które później będą droższe. Na przykład nie wybierałbyś bazy danych w locie, nie zastanawiając się nad swoimi potrzebami, nie budując i po prostu argumentując, że możesz ją później zmienić. Musisz to przemyśleć.

Brandon
źródło
1
Świetny punkt na weryfikację testu! Ponadto: budując dom, Trudno jest przesuwać ścianę (a to pozostawia blizny). Dzięki oprogramowaniu przenoszenie kodu jest stosunkowo łatwe - pod warunkiem, że masz zautomatyzowany test w celu wykrycia niezamierzonych zmian.
Technophile
4

Często się nad tym zastanawiałem, dopóki nie wykonałem kilku projektów TDD. Jest bardzo zwięzłe i wyczerpujące wyjaśnienie, które pojawiło się podczas mojej pracy:

Kiedy piszesz kod, musisz mieć pewien sposób, aby upewnić się, że kod robi coś znaczącego. Więc testujesz swój kod. Możesz przeprowadzać testy ad hoc. Jednak testowanie działa lepiej, jeśli pomyślisz o tym, jak przetestować, zanim zaczniesz coś robić. Więc projektujesz strategię testową, zanim przejdziesz do testowania.

Teraz, gdy myślisz o strategii testowania, możesz równie dobrze zautomatyzować, przynajmniej jej część ... I oto masz pewien poziom TDD. Fakt, że piszesz kod do momentu zdania testu, jest po prostu normalny. I tak to robisz, pisz kod, aż zadziała. Właśnie teraz łatwo jest przeprowadzić testy.

Z powodów poza zakresem nie piszesz całej rzeczy za jednym razem. Więc nie projektujesz testów za jednym razem. Ale w zasadzie to jest to. Po prostu lepsze planowanie testów, nie zawsze piszesz test z góry. Czasami dodajesz więcej w miarę postępów i znajdujesz błędy, których nie spodziewałeś się, lub czynisz test bardziej niezawodnym później. Czasami może się zdarzyć, że nie projektujesz testów, ale okaże się, że testy ręczne są pracochłonne, więc wykonujesz je później.

Tak więc TDD to tylko ekstremalny sposób na sprawdzenie, jak weryfikujesz swoją pracę.

joojaa
źródło
4

Chociaż pytanie to ma już zaakceptowaną odpowiedź, wydaje mi się, że mam coś do dodania, począwszy od stylu projektowania bez testów (wszystkie testy wykonywane ręcznie przez „testerów” zgodnie z procedurą testową) do TDD. To tylko moje osobiste obserwacje, choć uważam, że są dość uniwersalne.

Kiedy piszesz coś nowego, czego nigdy wcześniej nie robiłeś, nie ma znaczącej różnicy między TDD a brakiem TDD.

Ogólnie rzecz biorąc, należy zrobić mały fragment kodu, aby zbadać pomysł, a następnie dodać trochę zakodowanych bitów do „testowania”, a następnie skompilować i / lub wykonać. Jeśli zadziała, usuniesz zakodowane elementy i uogólnisz kod, dodając parametry, zmienne instancji itp.

Pomyśl o tym. To dokładnie tyle samo pracy, co TDD. Jedyna różnica polega na tym, że w TDD zapisujesz „testujące” bity osobno w innym pliku i nie usuwasz ich na końcu. W TDD zachowujesz swój kod testowy .

Oczywiście nieco bardziej zorganizowana TDD oznacza, że ​​jest nieco więcej pracy nad rozróżnieniem testowych bitów kodu od prawdziwego kodu. Ale jeśli wcześniej napisałeś testy jednostkowe, nauczysz się modulować kod do testowania.

Slebetman
źródło
2

Dlaczego zwinność polega na testowaniu testowym (TDD), a nie testowym (DDT)?

Po prostu ćwierkam tutaj, ponieważ uważam, że pytanie sugeruje fakt („zwinność dotyczy TDD”), co uważam za raczej niewłaściwe. Wydaje się, że wszystkie odpowiedzi przyjmują ten fakt za pewnik. Są to dobre odpowiedzi, jeśli uważasz, że zwinność dotyczy przede wszystkim TDD (czyli testowania na poziomie jednostki).

https://en.wikipedia.org/wiki/Agile_software_development#Agile_methods zawiera listę kilkunastu różnych modeli programistycznych.

Chciałbym zaproponować rozwój oparty na zachowaniach i rozwój oparty na funkcjach jako pokarm do przemyślenia. Zupełnie różne bestie, które również mogą prowadzić do wszystkich zalet podstawowego TDD, ale są dalekie od prostego cyklu refrakcji czerwono-zielonej.

Więc moja odpowiedź brzmi:

  • „DDT” (aka, pisanie testów po implementacji lub „nad nią”) nie działa z rzeczywistych powodów; gdy tylko pojawi się presja czasu lub pieniędzy, testy wychodzą z okna; a samo posiadanie testów na korzyść posiadania testów to raczej meh, IMO.
  • Zwinność nie polega wyłącznie na rozwoju opartym na testach (TDD), jeśli interpretujesz ten termin jako zasadniczo oznaczający „testy jednostkowe dla wszystkich elementów / klas” (co, przynajmniej według Wikipedii, ma miejsce). TDD to tylko jedna z możliwości sprawnego rozwoju.
  • Istnieją inne metody, takie jak BDD lub FDD, które wykorzystują podejście pełnego stosu. Nadal piszesz scenariusze z góry, nadal masz cykl refaktoryzacji czerwony-zielony i nadal wdrażasz tylko dopóki scenariusze nie są zielone, ale twoje „testy” z definicji ćwiczą całe oprogramowanie (działając jak interakcja użytkownika) i nigdy tylko jedna pojedyncza jednostka.

Wolę mieć aplikację z pełnym pokryciem BDD / FDD i bez testów jednostkowych, niż taką z pełnym pokryciem testów jednostkowych i bez testów pełnego stosu.

(TDD ma oczywiście swoje miejsce, na przykład w interfejsach API, ale nie o tym tutaj mówimy).

Znowu nie próbuję podążać za wszystkimi innymi odpowiedziami tutaj, tylko wskazując, że pytanie jest sformułowane raczej wąsko, a pole ma znacznie więcej do zaoferowania.

AnoE
źródło
Wiedziałem o drugim, ale w tym czasie pisanie tego wydawało mi się bardziej sensowne. Chcesz wyjaśnić, dlaczego TDD jest lepszy dla API? Czym różni się korzystanie z niego od klienta WWW z MVC?
nerdlyist
1
W interfejsach API niezwykle ważne jest, aby przestrzegały bardzo szczegółowej umowy ; każde wywołanie metody musi działać bardzo przewidywalnie, aby użytkownicy (którzy są innymi programami) mogli poprawnie z niego korzystać. Właśnie tam naprawdę świecą testy jednostkowe. OTOH, w aplikacji najważniejsze jest to, jakie funkcje użytkownicy muszą działać zgodnie z przeznaczeniem. Jasne, twoje „elementy wewnętrzne” również powinny być poprawne, ale ważniejsze jest, aby mieć dobry zasięg funkcji „od końca do końca” (tj. Od interfejsu użytkownika bezpośrednio do bazy danych i z powrotem) zamiast testować każdą indywidualną metodę (która użytkownicy końcowi i tak nie zobaczą).
AnoE
Oczywiście wszystko tutaj jest w różnym stopniu - sensowne może być tutaj rozproszenie testów jednostkowych, jeśli powinieneś gdzieś mieć bardzo skomplikowane metody / klasy (na przykład: obliczanie odległości między współrzędnymi geograficznymi); ale większość (np. w aplikacjach biznesowych) stanowi ogólny przepływ pracy.
AnoE
0

Chociaż nie widzisz go często pisanego w ten sposób, duża część uzasadnienia zwinnego polega na tym, że przepisywanie kodu jest lepsze niż pisanie go dobrze za pierwszym razem. Za każdym razem, gdy ponownie piszesz kod, poprawiasz go i poprawiasz siebie. Właściwe za pierwszym razem wydaje się być wolniejsze i bardziej kruche.

Analogia domu nie jest wcale taka zła, ale musisz pomyśleć o tym, jak długo wiemy o budowaniu domu, a jak długo wiemy o inżynierii oprogramowania - również złożoność inżynierii oprogramowania jest bliższa zbudowaniu długiego mostu lub 20 piętrowa wieża niż dom. Powinniśmy również wziąć pod uwagę, że przy tworzeniu nowych języków i narzędzi jest tak, jakby każdy konstruktor chciał zbudować budynek z zupełnie innych kształtów, rozmiarów i materiałów. Całkowity chaos.

Inspekcja nie jest jednak dobrą analogią do testowania kodu. W oprogramowaniu nie jesteśmy nawet do tego stopnia, że ​​mamy DOBRYCH inspektorów (z wyjątkiem twoich rówieśników o różnych poziomach umiejętności). Nie mamy również przepisów, które można by skontrolować, z wyjątkiem być może niektórych, w większości arbitralnych, „standardów kodowania” (co bardziej przypomina testowanie koloru farby i układu trawnika niż struktury).

Test-first bardziej przypomina budowanie ściany, a następnie wywarcie na nią pewnego nacisku w celu zapewnienia stabilności przed podniesieniem jej i umieszczeniem na domu. To tak, jakby zmierzyć okno, aby upewnić się, że będzie pasowało do otworu, który zostawiłeś przed próbą umieszczenia go.

Jeśli musiałbyś zbudować serię mostów bez naszej wiedzy na temat architektury, wzorców, przepisów i matematyki, które obecnie musimy udowodnić, że nasze mosty będą działać, zanim je zbudujemy, prawdopodobnie testowałbyś i odbudowywałeś DUŻO.

Wreszcie punkt niepodobny do analogii - za pomocą oprogramowania za każdym razem, gdy możesz ponownie napisać sekcję kodu, znacznie poprawiasz zarówno kod, jak i umiejętności. Wykorzystaj każdą okazję, aby ponownie napisać kod. TDD może być świetną wymówką.

Bill K.
źródło