TDD i kontrola wersji

25

Obecnie uczę się o TDD i staram się wprowadzić go w życie w moich osobistych projektach. Użyłem również kontroli wersji w wielu projektach. Interesuje mnie współdziałanie tych dwóch narzędzi w typowym przepływie pracy, szczególnie jeśli chodzi o maksymę, aby utrzymywać małe zobowiązania. Oto kilka przykładów, które przychodzą na myśl:

  1. Zaczynam nowy projekt i piszę prosty test, aby stworzyć klasę, która jeszcze nie istnieje. Czy powinienem wykonać test przed napisaniem klasy, nawet jeśli test się nawet nie skompiluje? A może powinienem usunąć minimalną ilość kodu potrzebną do skompilowania testu przed zatwierdzeniem?

  2. Znajduję błąd i piszę test, aby go odtworzyć. Czy powinienem zatwierdzić test zakończony niepowodzeniem, czy wdrożyć poprawkę błędu, a następnie zatwierdzić?

Są to dwa przykłady, które przychodzą mi na myśl. Podaj dodatkowe przykłady w swojej odpowiedzi.

Edytować:

W obu przykładach założyłem, że zaraz po napisaniu testu napiszę kod, aby test przeszedł pomyślnie. Może się również pojawić inna sytuacja: pracuję nad projektem przy użyciu TDD przez kilka godzin bez zobowiązań. Kiedy w końcu podejmuję decyzje, chcę podzielić moją pracę na małe części. (Git sprawia, że ​​jest to stosunkowo łatwe, nawet jeśli chcesz zatwierdzić tylko niektóre zmiany w jednym pliku).

Oznacza to, że moje pytanie dotyczy zarówno tego, co popełnić, jak i kiedy .

Code-Guru
źródło

Odpowiedzi:

21

Czy powinienem wykonać test przed napisaniem klasy, nawet jeśli test się nawet nie skompiluje? A może powinienem usunąć minimalną ilość kodu potrzebną do skompilowania testu przed zatwierdzeniem?

Oczywiście nie. Powinieneś zakończyć zarówno test, jak i klasę. Popełnienie czegoś 1 , że nawet nie kompilacji nie ma sensu, a na pewno, że ludzie pracują nad tym samym projektem zły, jeśli robisz to regularnie.

Znajduję błąd i piszę test, aby go odtworzyć. Czy powinienem zatwierdzić test zakończony niepowodzeniem, czy wdrożyć poprawkę błędu, a następnie zatwierdzić?

Nie, nie wykonuj testu negatywnego. Prawo LeBlanc stanowi:

Później równa się nigdy.

a test może się nie powieść przez długi czas. Lepiej naprawić problem, gdy tylko zostanie wykryty.

Ponadto styl programowania TDD mówi:

Rozwój oparty na testach nieustannie powtarza etapy dodawania przypadków testowych, które kończą się niepowodzeniem, przekazywania ich i refaktoryzacji.

Jeśli przejdziesz test zakończony niepowodzeniem, oznacza to, że nie ukończyłeś cyklu.


1 Kiedy powiedziałem commit, miałem na myśli naprawdę zobowiązanie do pnia (dla użytkowników git, wypchnij swoje zmiany, aby inni programiści je otrzymali).

BЈовић
źródło
4
„i na pewno sprawi, że gniewni ludzie będą pracować nad tym samym projektem, jeśli” - ktoś mieszka w świecie SVN, korzysta z GIT, a nikogo nie rozgniewasz
Mateusz
3
Myślę, że zatwierdzenie jest w porządku po napisaniu testu, po prostu nie wypychaj go, dopóki nie skończysz.
Matsemann
4
@radarbob Czy dotyczy to nawet DVCS, w którym istnieje różnica między popełnianiem a pchaniem? Mogę sobie wyobrazić sytuację, w której dokonuję kilku zmian w moim lokalnym repozytorium git, w których przy ostatnim zatwierdzeniu kompilacja nie jest zepsuta, ale może być w dowolnym tymczasowym zatwierdzeniu.
Code-Guru,
6
Nie, nie zatwierdzaj testu zakończonego niepowodzeniem. Ale jednym z punktów TDD jest właśnie wykonanie testu przed kodowaniem. Dlatego przeprowadzenie testu zakończonego niepowodzeniem ma sens.
mouviciel
4
@ Code-Guru: W przypadku DVCS, reguły powinny brzmieć: „Nie wysyłaj uszkodzonego kodu do gałęzi, z której inni regularnie ściągają”. Jeśli inni nie wycofają się z twojego lokalnego repozytorium, może to być w dowolnym stanie, w którym możesz żyć.
Bart van Ingen Schenau,
6

Czy powinienem wykonać test przed napisaniem klasy, nawet jeśli test się nawet nie skompiluje?

Nie.

Czy powinienem wykonać test negatywny

Nie.

Mówisz tutaj o dwóch paradygmatach:

  1. testowanie rozwoju - co nie mówi nic o zatwierdzaniu kodu. Rzeczywiście, mówi ci o tym, jak napisać kod i kiedy skończysz. Uważam więc, że każde „zrobione” jest kandydatem do zatwierdzenia.
  2. zwinny rozwój, w szczególności: „wcześnie i często dokonuj” (co nie wymaga TDD). Ideą jest wczesna integracja z innymi komponentami w systemie, a tym samym wczesna informacja zwrotna. Jeśli wpiszesz DVCS lokalnie i nie będziesz naciskać, to nie ma sensu w tym znaczeniu. Lokalne zobowiązania tylko pomagają programistom ustrukturyzować swoją pracę.

Moje zalecenie jest następujące: podążaj za kółkiem TDD, aż kod się skompiluje, twoje testy są zielone i masz coś do wniesienia do systemu. Dlatego powinieneś wyciąć swoje funkcje w pionie, np. W przypadku nowej maski interfejsu użytkownika nie twórz całego formularza i zatwierdzaj bez logiki biznesowej, ale raczej zaimplementuj jeden mały aspekt, ale w interfejsie użytkownika, a także logikę biznesową, a także w warstwie trwałości .

W przypadku dużej poprawki, zatwierdzaj po każdej poprawce (np. Refaktoryzacji), nawet jeśli błąd nie został jeszcze naprawiony. Testy powinny być zielone, a kod musi się jednak kompilować.

Andy
źródło
5

Na pewno zaczniesz od używania zdrowej kontroli źródła, takiej jak git.

Następnie możesz pracować tak, jak chcesz i zatwierdzać na każdym rogu - każdy krok lub podetap to uczciwa gra.

Następnie przed popchnięciem rzeczy zgniatasz całą pracę w jednym zatwierdzeniu. Lub para w punktach, w których wszystko jest zielone, a kompozycja ma sens. I popchnij te rozsądne zobowiązania. W przypadku wielu przypadków utwórz gałąź, którą scalisz z --no-ff.

Kontrola źródła nie jest systemem śledzenia pracy ani historykiem. Zatwierdzenia powinny wykazywać rozsądną spójną deltę, podczas gdy stan kasy powinien być co najmniej kompilowany. Pośrednie mogą być przez pewien czas zachowywane do celów przeglądu, ale gdy wszystko zostanie uznane za prawidłowe, pojedynczy zatwierdzenie na funkcję jest sprawiedliwy.

Balog Pal
źródło
5

W moim rozumieniu świata zobowiązuje się zaznaczyć punkt, do którego może być pożądany powrót. Punkt, w którym test się nie udaje (ale się kompiluje) jest zdecydowanie jednym z takich punktów. Gdybym miał odejść w złym kierunku, próbując zdać test, chciałbym móc przywrócić kod z powrotem do punktu początkowego i spróbować ponownie; Nie mogę tego zrobić, jeśli się nie popełniłem.

Scroog1
źródło
Zgadzam się z Tobą. Wolę użyć innej gałęzi, aby postępować zgodnie z zasadą „nie przerywaj kompilacji” i scalać zmiany w linii głównej dopiero po przejściu testu.
Fil
5

Czy powinienem wykonać test przed napisaniem klasy, nawet jeśli test się nawet nie skompiluje?

Z rozgałęziającym się SCM (widziałem, że używasz Gita), powinieneś zatwierdzać, kiedy chcesz punkt zapasowy („Zepsułem coś; zresetuję działający katalog do ostatniego punktu kopii zapasowej”) lub gdy masz stabilną wersję. Jeśli masz stabilną wersję (wszystkie testy przeszły pomyślnie), powinieneś również rozważyć połączenie bieżącej gałęzi funkcji z główną gałęzią programistyczną.

A może powinienem usunąć minimalną ilość kodu potrzebną do skompilowania testu przed zatwierdzeniem?

Od ciebie (git daje ci elastyczność, którą możesz popełniać, kiedy tylko chcesz, bez wpływu na innych członków zespołu lub twoją zdolność do pracy nad różnymi funkcjami). Tylko upewnij się, że nie masz wielu niekompletnych (niedziałających) funkcji w tym samym oddziale w tym samym czasie (wtedy będą się wzajemnie blokować).

Znajduję błąd i piszę test, aby go odtworzyć. Czy powinienem zatwierdzić test zakończony niepowodzeniem, czy wdrożyć poprawkę błędu, a następnie zatwierdzić?

Zazwyczaj dokonuję dwóch zatwierdzeń, chyba że kod testowy jest naprawdę mały / trywialny do napisania.

Są to dwa przykłady, które przychodzą mi na myśl. Podaj dodatkowe przykłady w swojej odpowiedzi.

Edytować:

W obu przykładach założyłem, że zaraz po napisaniu testu napiszę kod, aby test przeszedł pomyślnie.

To może być złe założenie. Jeśli pracujesz sam (osobisty projekt), nic nie stoi na przeszkodzie, abyś zawsze to robił. W jednym z moich najbardziej udanych projektów (w odniesieniu do utrzymywania wysokiej jakości kodu i TDD podczas opracowywania projektu) czasami definiowaliśmy testy na kilka tygodni przed ich wdrożeniem (tzn. Powiedzielibyśmy, że „test” test_FOO_z_null_first_parameter ”jest teraz definiowany jako pusta funkcja i tak to robimy). Następnie robiliśmy sprint (lub pół sprintu) czasami miesiąc lub więcej później, po prostu w celu zwiększenia zasięgu testu dla modułu.

Może się również pojawić inna sytuacja: pracuję nad projektem przy użyciu TDD przez kilka godzin bez zobowiązań. Kiedy w końcu podejmuję decyzje, chcę podzielić moją pracę na małe części. (Git sprawia, że ​​jest to stosunkowo łatwe, nawet jeśli chcesz zatwierdzić tylko niektóre zmiany w jednym pliku.) Oznacza to, że moje pytanie dotyczy zarówno tego, co, jak i, kiedy to zrobić.

Powiedziałbym zdecydowanie zobowiązać się do tworzenia punktów zapasowych . Działa to bardzo dobrze w przypadku testów eksploracyjnych („Dodam tylko kilka wydruków w całej bazie kodu, uruchomię je i git reset --hardusunę, gdy skończę) oraz do prototypowania.

utnapistim
źródło
2
Zachowaj ostrożność przy polecaniu git reset --hard. Jest to jedno z niewielu poleceń w git, które spowoduje utratę pracy.
gnash117
2

W mojej pracy, gdy tylko jest to możliwe, wykonuję niepewną pracę w osobistym oddziale kontroli źródła. Więc mogę spróbować, nie powiodło się, spróbuj ponownie, jeśli to konieczne, aż zadziała, i zatwierdzaj większy projekt tylko wtedy, gdy mam rzeczywisty działający kod.

Z perspektywy TDD pytanie „najpierw zameldujesz się w teście?” zależy całkowicie od kodu, nad którym pracujesz. Jeśli jest to nowy kod, niczego nie zameldujesz, dopóki nie będziesz mieć czegoś wartego zameldowania. Ale jeśli jest to błąd znaleziony w już skompilowanym lub wysłanym kodzie, warto sprawdzić w teście odtworzenia błędu BY BYŁO SAMODZIELNIE. Zwłaszcza jeśli jest to koniec dnia roboczego i opuścisz biuro przed naprawą kod.

(Oczywiście, jeśli Twój sklep ma zautomatyzowany proces kompilacji, który umiera, jeśli testy jednostkowe zakończą się niepowodzeniem, możesz nie chcieć sprawdzać testu zakończonego niepowodzeniem, dopóki nie naprawisz błędu. Ale to wydaje się dziwny sposób działania, ponieważ „znajdź oraz błędy w dokumentach ”i„ napraw błędy ”mogą być wykonywane przez dwa całkowicie różne zespoły).

DougM
źródło