Jakie są wady pisania kodu przed pisaniem testów jednostkowych?

33

Zawsze widziałem zalecenie, aby najpierw napisać testy jednostkowe, a następnie zacząć pisać kod. Ale czuję, że pójście w drugą stronę jest dla mnie znacznie wygodniejsze - pisz kod, a następnie testy jednostkowe, ponieważ czuję, że po napisaniu właściwego kodu mamy znacznie większą przejrzystość. Jeśli napiszę kod, a następnie testy, być może będę musiał nieco zmienić kod, aby był testowalny, nawet jeśli dużo się skoncentruję na stworzeniu projektu do przetestowania. Z drugiej strony, jeśli napiszę testy, a następnie kod, testy będą się zmieniać dość często w miarę kształtowania się kodu.

Ponieważ widzę wiele zaleceń, aby rozpocząć pisanie testów, a następnie przejść do kodowania, jakie są wady, jeśli zrobię to inaczej - napisz kod, a następnie testy jednostkowe?

k25
źródło
7
+1 za pytanie, dlaczego dana praktyka jest „najlepszą praktyką” przed jej przyjęciem
TaylorOtwell 13.01.11

Odpowiedzi:

37

Odpowiedź jest czerwona. Czerwony jest tym, co otrzymujesz z cyklu refrakcji TDD czerwono-zielonego, którego nie możesz uzyskać, w testach. Najpierw napisz test zakończony niepowodzeniem. Zobacz, jak się nie udaje. To twoja czerwona i to ważne. Mówi: Mam ten wymóg i wiem , że mój kod go nie spełnia. Kiedy więc przejdziesz do kroku 2 (zielony), z taką samą pewnością wiesz, że Twój kod spełnia ten wymóg. Wiesz, że zmieniłeś bazę kodu w taki sposób, aby spełnić wymagania.

Wymagania (testy) opracowane po kodzie, oparte na kodzie, pozbawiają cię tego rodzaju pewności, pewności.

Carl Manaster
źródło
+1 - doskonały punkt i punkt zajęty! Dziękuję za Twoją opinię!
k25 13.01.11
7
Testy! = Wymagania. Zarówno testy, jak i kod powinny pochodzić z wymagań.
Bart van Ingen Schenau
2
@Bart van Ingen Schenau: Siła TDD jest dokładnie tym, co sprawdza wymagania ARE. Ponadto są to wymagania dotyczące wykonywalności.
mouviciel
1
@Bart: Testy jednostkowe są często zbyt szczegółowe dla wymagań (wysokiego poziomu) klientów, ale pomysł zdecydowanie się utrzymuje, zwłaszcza jeśli weźmiemy również pod uwagę testy wyższego poziomu, takie jak automatyczne testy akceptacyjne, które po napisaniu powinny być ostatecznymi wymaganiami. To jest istota „zwinnego testowania akceptacji”.
Martin Wickman
3
TDD nie polega na testowaniu, ale na specyfikacji. Testy oparte na TDD są środkiem komunikacji między deweloperem a klientem w celu uzgodnienia, jaki produkt powinien zostać wykonany.
mouviciel
18

Jeśli napiszesz kod, a następnie testy, to zbyt łatwo wpaść w pułapkę pisania testów, aby kod przeszedł, zamiast pisania testów, aby upewnić się, że kod spełnia specyfikację.

To powiedziawszy, zdecydowanie nie jest to jedyny sposób na robienie rzeczy i nie ma „najlepszego” sposobu tworzenia oprogramowania. Jeśli włożysz dużo pracy w opracowanie przypadków testowych, nie wiesz, czy proponowana architektura ma jakieś wady do znacznie później - podczas gdy najpierw opracujesz kod, napotkasz je wcześniej i możesz przeprojektować z mniej zatopionymi wysiłek.

Zaraz.
źródło
Tak, masz rację co do pierwszego punktu, ale zawsze upewniam się, że tego nie robię. Jeśli test się nie powiedzie, zawsze przechodzę do kodu i upewniam się, że jest poprawny, a następnie sprawdzam, czy mój test jest poprawny, a następnie modyfikuję ten, który jest nieprawidłowy. Dziękuję za twoją opinię. Będę o tym pamiętać. +1 ode mnie za 1. punkt i ostatni punkt ...
k25 13.01.11
2
Ale co jeśli test się powiedzie? Test może się zdać, ponieważ w rzeczywistości nie wykonuje kodu zainteresowania; tak naprawdę nie może się zdarzyć w TDD, ponieważ test powinien początkowo zakończyć się niepowodzeniem, a tak się dzieje - jeśli nie, nie przejdziesz do kroku 2, dopóki go nie naprawisz. Tak więc w trybie testowym jest ostatni tryb awarii, który najpierw nie istnieje.
Carl Manaster
@Carl Manaster - Tak, masz ważny punkt. Po napisaniu kodu doskonale zdaję sobie sprawę z wymagań, więc mój test jednostkowy byłby odpowiedni (idealnie). Jeśli mój przypadek testowy przejdzie pomyślnie, powiedziałbym, że kod jest poprawny, jeśli test się nie powiedzie, podążę za tym, co powiedziałem. Ale w 100% zgadzam się, że masz do tego rację.
k25 13.01.11
@ k25: Chodzi o to, że jeśli Twój test zakończy się pomyślnie, nadal nie wiesz, czy kod jest poprawny, czy nie. Przypadek testowy może być nieprawidłowy.
Anon.
@Zaraz. - tak, masz rację, wezmę również tę sprawę pod uwagę.
k25
12

W rzeczywistości ludzie rozłączają się na TDD, chodzi o testowanie, chociaż zapominają o dwóch pozostałych literach w akronimie. Coś, co można przeczytać tutaj: TDD bez T lub TDD nie dotyczy testowania .

Chodzi o to, że nauczyłem się wielu innych rzeczy, które są ściśle powiązane z TDD. Nie ma znaczenia, jeśli najpierw wykonasz test: ważne jest myślenie o projektowaniu oprogramowania .

Aby móc nawet pisać testy jednostkowe „we właściwy sposób”, tzn. Aby były one izolowane, szybkie i zautomatyzowane, miejmy nadzieję, że zauważysz, że trzeba trochę przemyśleć sposób ułożenia kodu w taki sposób, aby kod stał się łatwiejszy testować.

Osobiście nauczyłem się zasad SOLIDNYCH, nie wiedząc, że coś takiego zostało napisane. Wynika to z faktu, że pisanie testów jednostkowych zmusiło mnie do przepisania klas, aby nie były zbyt skomplikowane do testowania. Doprowadziło to do:

  • Musiałem przenieść funkcjonalność, która albo nie miała sensu, albo opierała się na prywatnych metodach, na osobne klasy, aby móc je przetestować osobno. (Zasada pojedynczej odpowiedzialności).
  • Musiałem uniknąć dużych struktur dziedziczenia i zamiast tego rozszerzyć implementacje o kompozycję (widoczne w zasadzie Open-Closed).
  • Musiałem sprytnie podchodzić do dziedziczenia, korzystałem z klas abstrakcyjnych za każdym razem, gdy widziałem wspólny kod, który można było udostępniać, i stosowałem metody pośredniczące (zasada podstawienia Liskowa).
  • Musiałem pisać interfejsy i klasy abstrakcyjne, aby móc testować klasy osobno. Co nieumyślnie prowadzi do pisania fałszywych obiektów. (Zasada segregacji interfejsu)
  • Ponieważ napisałem wiele interfejsów i klas abstrakcyjnych, zacząłem deklarować zmienne i parametry, aby użyć wspólnego typu (zasada inwersji zależności).

Mimo że cały czas nie przeprowadzam testów, zdarza mi się przestrzegać dobrych zasad OO i praktyk, które zaczynasz stosować, aby ułatwić testowanie. Teraz nie piszę kodu dla samego siebie. Napisałem kod, aby można go było łatwo przetestować lub co ważniejsze; łatwe w utrzymaniu .

Łup
źródło
1
+1 dla SOLID naturalnie przychodzi ci na myśl, gdy myślisz o projektowaniu oprogramowania.
ocodo
+1 (właściwie chciałem dać +10, ale nie mogę). Dokładnie moje myśli - lista punktów była bardzo dobra. To jeden z powodów, dla których zadałem to pytanie. Czułem, że zajęcia stały się znacznie bardziej, kiedy zacząłem pisać testy jednostkowe po napisaniu kodu. Ale chciałem zobaczyć zalety / wady obu stron, dziękuję za wasze opinie!
k25
10

Wszystkie pozostałe odpowiedzi są dobre, ale jest jeden punkt, który nie został poruszony. Jeśli najpierw napiszesz test, gwarantuje to, że testy zostaną napisane. Po napisaniu działającego kodu kuszące jest, aby pominąć testy i po prostu zweryfikować je za pomocą interfejsu użytkownika. Jeśli masz dyscyplinę, aby zawsze mieć test zakończony niepowodzeniem przed napisaniem kodu, możesz uniknąć tej pułapki.

RationalGeek
źródło
4

Jeśli najpierw napiszesz testy, dajesz kolejną szansę na przemyślenie swojego projektu, zanim ten projekt zostanie „odlany w kamieniu”.

Na przykład możesz pomyśleć, że potrzebujesz metody, która wymaga określonego zestawu parametrów. A jeśli najpierw napiszesz kod, napiszesz go w ten sposób i dostosujesz test do określonych parametrów. Ale jeśli najpierw napiszesz test, możesz pomyśleć „poczekaj chwilę, nie chciałbym używać tego parametru w kodzie głównym, więc może powinienem zmienić interfejs API”.

Zaraz
źródło
+1 za pierwszy punkt. Ale nie przechodząc do poziomu parametrów, co się stanie, jeśli projekt zostanie omówiony z innymi i zaakceptowany?
k25
@ k25 - jeśli coś jest trudne w użyciu zgodnie z przeznaczeniem, wymaga więcej przemyśleń. Czasami - bardzo rzadko - jest to po prostu trudne zadanie. Ale częściej można to zredukować do prostszych zadań. Nie mam linku, ale Gosling lub Goetz przeprowadzili wywiad na temat projektowania API kilka lat temu ... warto Googling.
Anon,
jasne
2

Ponieważ widzę wiele zaleceń, aby zacząć pisać testy, a następnie przejść do kodowania,

Jest to naprawdę dobry powód.

Jeśli powiesz „rób to, co uważasz za słuszne”, ludzie robią najgłupsze i najbardziej szalone rzeczy.

Jeśli powiesz „najpierw napisz testy”, ludzie mogą przynajmniej postąpić właściwie.

jakie są wady, jeśli zrobię to inaczej - napisz kod, a następnie testy jednostkowe?

Zwykle kiepski test i projekt, który należy przerobić, aby można go było przetestować.

Jest to jednak tylko „zwykle”. Niektóre osoby rozwijają projekty i testy równolegle. Niektóre osoby wprowadzają testowalny kod i piszą testy bez przeróbek.

Zasada „Najpierw test” ma na celu nauczanie i instruowanie ludzi, którzy nie mają pojęcia.

W podobny sposób przed przejściem przez ulicę zawsze mówi się „w obie strony”. Jednak tak naprawdę nie. I to nie ma znaczenia. Mieszkam w kraju z kierownicą po prawej stronie i muszę tylko patrzeć w lewo, kiedy zaczynam przekraczać.

Kiedy odwiedzam kraj z kierownicą po lewej stronie, tylko patrzenie w lewo może mnie zabić.

Zasady są określone bardzo mocno z jakiegoś powodu.

To, co robisz, to twój własny problem.

S.Lott
źródło
2

chodzi o to, żeby najpierw napisać test, żebyś pomyślał

  • jak przetestować kod
  • interfejs, który kod musi przedstawić, aby był testowalny

jeśli robisz coś prostego, prawdopodobnie nie ma znaczenia, który piszesz jako pierwszy (chociaż dobrze jest pielęgnować nawyk pierwszego testu), ponieważ test będzie prosty, a interfejs będzie oczywisty

ale TDD można skalować do testów akceptacyjnych, nie tylko testów jednostkowych, a wtedy interfejs staje się nietrywialny.

Steven A. Lowe
źródło
1

Po pierwsze, jeśli najpierw nie napiszesz testów, to nie wykonujesz Test Driven Development (TDD). Korzyści są liczne i często trudno w to uwierzyć, dopóki nie przećwiczysz go wiele razy. Oto korzyści, które otrzymałem dzięki TDD w porównaniu z tradycyjnym rozwojem:

  1. Siatka bezpieczeństwa testów - pozwala na dokonywanie dużych zmian bez obawy, że coś się nie stanie
  2. Projekt ekologiczny - projekt, który kończę, jest zwykle inny niż projekt, który zrobiłbym od zera i zawsze był lepszy
  3. Produktywność - praca nad małymi celami (zaliczenie tego jednego testu) i sprawienie, by (wszystkie testy zdały) działa dla mnie naprawdę dobrze i motywuje mnie do działania. Dodajcie parę, a moja wydajność osiągnie nowy poziom.

Książki: Beck, K. Test-Driven Development by Przykład

Dobry przykład: http://jamesshore.com/Blog/Lets-Play/

Mike Polen
źródło
+1 - fajne punkty (szczególnie 1.) i dzięki za linki!
k25
0

Kiedy piszesz test, skąd wiesz, że wykryje warunek niepowodzenia? Odpowiedź brzmi „przetestuj test”. Jak to zrobić, to najpierw napisać test, zobaczyć, że się nie udaje, i zobaczyć, że przejdzie on pomyślnie, gdy testowana jednostka zostanie pomyślnie zakodowana (cykl czerwony / zielony / refaktor wspomniany w jednej z pozostałych odpowiedzi).

Najpierw napisanie kodu, a potem test pozostawia otwarte pytanie, czy test pokaże uczciwe niepowodzenie.

Pamiętaj, że twoje testy wyrażają specyfikację. Jeśli musisz zmienić swoje testy, gdy kod „kształtuje się”, sugeruje to zmianę specyfikacji. To może, ale nie musi, być dobrą rzeczą. Może to oznaczać, że Twoje zrozumienie problemu nie było początkowo prawidłowe. Z drugiej strony może to oznaczać, że testujesz „jak” jednostka wykonuje swoją pracę, a nie to , co powinna osiągnąć.

Zenilogix
źródło