TDD - a rzeczywistość

0

Witam,

jestem w trakcie lektury "Mistrz czystego kodu" Robert C. Martin i napisał on:

//"1. Nie wolno napisać Ci nawet wiersza produktywnego kodu, jeżeli wcześniej nie napiszesz pasującego testu jednostkowego, który nie zostanie zaliczony.
2. Nie wolno Ci napisać więcej testu jednostkowego, niż jest konieczne, żeby test nie został zaliczony. Nieudana kompilacja też powoduje, że test nie jest zaliczony.
3. Nie wolno Ci napisać więcej produktywnego kodu, niż potrzeba, żeby aktualnie oblany test został zaliczony."
//

Czy w praktyce jak piszecie aplikacje jakiekolwiek to stosujecie się to tych trzech zasad zawodowo i prywatnie??

3

W pracy praktycznie 100% ( wyjątki to jakis naprawdę stary kod, kóry ma zaraz umrzeć , generalnie jak do zakończenia działania czegoś jest mniej niż 2 miesiące - to można czasem olać testy).

W projektach domowych róznie:

  • czasem stosuje złośliwe "TDD (kreatywność w punkcie 3)" - generalnie nie do końca polecam wobec kolegów, bo się odbija czkawką - ale jako ćwiczenie lubię,
  • w Scali staram się czasem przepisać testy na typy (średnio mi to wychodzi),
  • mam taki projekt, w którym naprawdę nie wiem co robię - wtedy też nie piszę testów,
  • czasem olewam testy zupełnie - stosuję zasadę 2 miesięcy - testy mniej więcej po takim czasie się zwracają - jak jest strzał na kilka dni i masz gwarancję, że kod po dwóch miesiącach nie bedzie dalej utrzymywany , i nie będzie działał - to można testy oszczędzić,

Natomiast pisanie testów po implementacji jest bzdurą. Czasami jak mi ktoś w zespole powie, że wszystko działa i tylko musi teraz napisać testy -- to mówię, żeby tego nie robił. Te testy "po", już niczego nie wnoszą. Tak się zdarza, jak jest ktoś nowy, albo pracuje nad nową technologią i nie ogarnia jak w niej testować.
Natomiast absolutnie wymagam testów po zraportowanym bugu - był bug w kodzie - to najpierw test odtwarzający bug - potem fix. (Bug driven tests).
Takie testy są najbardziej cennne. Czasami to jest całkiem niezłe wyzwanie, jak był np. bug na współbieżności, czasem się niestety też nie da - niektóre Heisenbugi.
( no i nadal w roku 2016 nie mam testów do CSS- czyli wywali się layout - jak to przetestować? JavaScript , typescript natomiast testuje się nawet lepiej niż Javę).

0

Mam wrazenie, ze jak ktos autorytarnie nie zarzadzi, ze najpierw testy pozniej implementacja to ludzie nie naucza sie w ten sposob pisac. Pozniej jest to 'wola programisty' i nikt juz nie robi TDD bo zwolennicy poddaja sie jak pozniej maja wszystko refaktoryzowac.

Moze TDD nie jest idealne ale jest jednak lekarstwem na wiele rzeczy.

  • porzadkuje kod, lepszy design
  • single responsibility
  • jest wiecej testow ;)
  • wieksza czytelnosc, nazwy testow mowia co ma robic kod
  • im dalej w las tym bardziej czas na testy sie zwraca
0

to wychodzi na to że ucząc się nowego języka powinno się jednocześnie przerabiać testowanie w nim jak i samą składnie języka? od kiedy "programuje" czy to w pascalu kiedyś czy c/c++ czy innym języku nigdy nie spotkałem ani książki ani kursu, która by poruszała "oba" problemy jednocześnie. A wydaje mi się (mogę się mylić) że ucząc się języka nabieramy jakiś przyzwyczajeń itp i potem wprowadzając do tego testy musimy uczyć się na nowo "pisania". Pozbyć się jakiś przyzwyczajeń itd.

0
rav3n napisał(a):

Mam wrazenie, ze jak ktos autorytarnie nie zarzadzi, ze najpierw testy pozniej implementacja to ludzie nie naucza sie w ten sposob pisac. Pozniej jest to 'wola programisty' i nikt juz nie robi TDD bo zwolennicy poddaja sie jak pozniej maja wszystko refaktoryzowac.

U mnie to nie działało. To co działa to wyszkolenie managementu w temacie "dług techniczny".
A na programistów... jedynie manipulacja :-(

0

Testy jednostkowe to must have. Im latwiej to robic w tecnologii X tym wiecej zyskuje w moich oczach.

Np. Golang.z dodatkowym libem jest bardzo spoko pod wzgledem testow i od razu mi zaplusowal w tym miejscu ;)

Latwo w internecie znajdziesz jak napisac prosty test w jezyku X.. Poczytaj o podejsciu TDD i zmus sie do pisania testow najpierw. Dobre IDE tez pomaga. Piszac test z metodami ktore nie istnieja i tworzysz puste lub rzucajace wyjatkami 'not implemented yet' generujac je z IDE. I zeby nie przeszedl Ci test bez implementacji czasem ;)

0

to że testy są potrzebne wiem doskonale tzn rozumiem ich idee itp:) jakiś czas temu napisałem pewną aplikację na własny użytek (ma około 2000 linii kodu nie wiele no ale:) ) i właśnie się zastanawiam czy nie spróbować napisać tego jeszcze raz pisząc do tego testy. Tylko właśnie, uczyłem się cały czas np. javy ale bez JUnit np. bo przecież testy to nie tylko assertEquals przecież.

Przez chwilę przeszło mi przez myśl nawet napisanie testów do napisanej aplikacji... ale po chwili w mojej głowie pojawiało się inne rozwiązanie danego problemu i testy wymusiły by na mnie zmianę całego kodu.

0

TDD jest jak Vim.

  1. zupełnie inne programowanie, żeby skutecznie pracować z TDD/Vimem trzeba się nauczyć nowych nawyków, zanim się nauczymy, będzie to niewygodne
  2. TDD jak i Vim ma dużo zalet i często przyśpieszają pracę (TDD = szybka pętla zwrotna, ochrona przed regresją, łatwość refaktoringu, Vim = potęga edycji, inteligentne komendy itp.)

Jednak zarówno jeśli chodzi o TDD jak i Vim, to nie osiągnąłem biegłości w żadnym z nich, więc korzystam z niemodalnego edytora (teraz z Atoma, ale robię już własny), a kod piszę w ten sposób, że najpierw jest hakerka(make it work), potem jest przemyślenie architektury(make it right), i dopiero na tym etapie piszę testy (inaczej musiałbym przepisywać testy kilka razy, bo między pierwszą a drugą fazą często zmieniam API danego modułu).

Chociaż muszę się zgodzić, że do fiksowania bugów TDD bardzo dobrze się sprawdza:

Natomiast absolutnie wymagam testów po zraportowanym bugu - był bug w kodzie - to najpierw test odtwarzający bug - potem fix. (Bug driven tests).
Takie testy są najbardziej cennne.

Szybki feedback "czy już działa" oraz zabezpieczenie przed pojawieniem się tego samego buga w przyszłości.

Także TDD sprawdza się u mnie jeśli wiem co robię (zwykle jednak jestem jak ten pies w stanie nieważkości z napisem "I have no idea what I'm doing")

0

Ja tego trochę sobie nie wyobrażam. Przecież pisząc kod... nie wiemy jak on będzie jeszcze wyglądał. Czasami zdarza się 10 razy zmieniać sygnaturę jakiejś metody czy przerabiać jedną klasę... to jak ja mam przewidzieć jak kod ma wyglądać przed jego napisaniem? Bo tego wymaga ode mnie TDD.

0

Z TDD najpierw definiujesz zachowania, a potem je implementujesz.

5

@jarekr000000

Te testy "po", już niczego nie wnoszą

Ktoś chyba nigdy nie słyszał o czymś takim jak regresja albo o błędach zwiazanych z refaktoryzacją kodu. Serio czasem czytając twoje posty zastanawiam sie czy pracowałeś dłużej przy jakimś (starszym) projekcie w fazie utrzymania. Staż by sugerował że musiało tak być, ale to co piszesz nijak...
Oczywiście jeśli ktoś chce ocenić jak przydatne są testy w chwili ich napisania zaraz po implementacji to wartość jest zerowa, o ile programista nie jest idiotą i nie napisał niedziałajacego kodu. Ale wartość testów wychodzi znacznie później, kiedy ktoś zaczyna coś refaktorować, zmieniać technologię, migrować itd.

Natomiast absolutnie wymagam testów po zraportowanym bugu

Ja mam nadzieje ze to jest standard wszędzie ;)

@rav3n

  • porzadkuje kod, lepszy design

Nigdy nie widziałem żeby podejscie test-first wygenerowało lepszy kod. Z mojego dświadczenia wynika ze generuje taki sam.

  • single responsibility

Nie widzę związku.

  • jest wiecej testow
  • wieksza czytelnosc, nazwy testow mowia co ma robic kod
  • im dalej w las tym bardziej czas na testy sie zwraca

To samo można powiedzieć o pisaniu testów w ogóle, niezależnie od tego czy to TDD czy cokolwiek innego. Ty chyba próbujesz przeciwstawić tutaj "pisanie testów" od "niepisania testów".

Przecież pisząc kod... nie wiemy jak on będzie jeszcze wyglądał

I nie musisz, bo chodzi tu raczej o testowanie "zachowania" czy też taki blackbox test. Więc interesuje cię co funkcja robi a nie jak wygląda od środka. Wiec generalnie tylko sama sygnatura ma jakieśtam znaczenie, żebyś mógł to sobie wywołać w teście. To zresztą jest też potem takie trochę programowanie top-down. Tzn najpierw definiujesz sobie same sygnatury a potem dopiero zaczynasz wypełniać je implementacją.

0

Przy odpowiednio ekspresywnym systemie typów testy jednostkowe nie są potrzebne. Jako skrajne przykłady można podać np. Coq, w którym poprawność programu jest całkowicie weryfikowana podczas kompilacji, i języki dynamiczne, w których można pisać nieskończoność testów i nie mamy nigdy pewności że się wszystko nie posypie.

Albo inaczej mówiąc, im jest mniej możliwych rozwiązań danego problemu, tym mniej testów trzeba pisać, a typy ograniczają tą przestrzeń rozwiązań. Na przykład funkcja o sygnaturze (przykład w Scali):
def map[A, B](f: A => B, a: A): B

Ma tylko jedno możliwe rozwiązanie, dlatego nie ma sensu jej testować.

0

@adwy

  1. Coq to nie język programowania tylko system dowodzenia twierdzeń, wiec niejako z definicji to tam działa.
  2. Nie zgodzę sie że podana funkcja nie wymaga testu. Bo przecież nie trudno ją zepsuć zwracając sobie z niej losowy obiekt typu B, wcale nie będący wynikiem f(a). System typów ładnie "testuje" na przykład spinanie sie ze sobą modułów / interfejsów. Ale końcowa implementacja zwykle i tak wymaga testu bo gdzieś tam trzeba sprawdzić czy faktyczny wynik jest poprawny.
0
Shalom napisał(a):

Nie zgodzę sie że podana funkcja nie wymaga testu. Bo przecież nie trudno ją zepsuć zwracając sobie z niej losowy obiekt typu B, wcale nie będący wynikiem f(a).

Nie da się w ten sposób popsuć, bo nie wiesz czym jest B i w jaki sposób zwrócić jakiś losowy element tego typu.

Shalom napisał(a):

Ale końcowa implementacja zwykle i tak wymaga testu bo gdzieś tam trzeba sprawdzić czy faktyczny wynik jest poprawny.

Ale im więcej wynika z typowania tym mniej rzeczy trzeba zweryfikować. Przykładowo jeżeli wiemy że jakaś funkcja jako wynik powinna zwrócić listę z przynajmniej jednym elementem, to jeżeli zwróci NonEmptyList zamiast zwykłego List, to nie trzeba już pisać testu który to sprawdza.

0
Shalom napisał(a):

@jarekr000000

Te testy "po", już niczego nie wnoszą

Ktoś chyba nigdy nie słyszał o czymś takim jak regresja albo o błędach zwiazanych z refaktoryzacją kodu. Serio czasem czytając twoje posty zastanawiam sie czy pracowałeś dłużej przy jakimś (starszym) projekcie w fazie utrzymania. Staż by sugerował że musiało tak być, ale to co piszesz nijak...
Oczywiście jeśli ktoś chce ocenić jak przydatne są testy w chwili ich napisania zaraz po implementacji to wartość jest zerowa, o ile programista nie jest idiotą i nie napisał niedziałajacego kodu. Ale wartość testów wychodzi znacznie później, kiedy ktoś zaczyna coś refaktorować, zmieniać technologię, migrować itd.

Oj @Shalom - jak co nie pasuje do twojej wizji to musi być głupek pisał ... podoba mi się twój tok rozumowania.

Ale ciekawostka - od nastu lat najwięcej zarabiam właśnie na nekromancji - czyli utrzymaniu w ruchu - a nawet rozwoju - mniej lub bardziej starych systemów (choć ostatnio mam wrażenie, że 3 lata to już stary - ale powiedzmy, że obecnie np. tykam kodu mającego max 8 lat - gdzie pare pokoleń programistów się przewinęło... ;-)). Przy okazji: te "stare" systemy nawet lubię: tu są wyzwania :-).

Wnioski wyciągam na podstawie... pieniędzy - w działającym, rozwijanym systemie zawsze jest mnóstwo miejsc, które można poprawić - i trzeba priorytetyzować - co najbardziej się opłaca. Pisanie testów po implementacji uznałem już dawno za nic nie wnoszące - bo się zwykle nie wywalają.

A co w przypadku refaktoryzacji? no właśnie - jak ktoś taką robi na kodzie, który nie ma testów to właśnie jest moment zapłaty. Zanim się zacznie - trzeba napisać testy. Dokładnie tak samo w przypadku bugu.

Testy zrobione "zaraz po implementacji" i tak mam w całej masie, i jak to w testach robionych po -> wszystko jest na Mockito. Nawet po usunięciu implementacji potrafią nadal zakończyć się sukcesem :-) (A próba refaktoryzacji niezmiennie kończy się koniecznością jednoczesnej zmiany i kodu i testów... czyli totalnie bez sensu).

0

Ostatnio muszę pracować z systemem, którego nawet zbudowanie jest wyzwaniem. Nie używa ant ani maven. Nie jest nawet pakowany jako .jar. To jest czysta nekromancja: muszę zrozumieć live coding na Tomcat. Może uda się coś z tym zrobić. Wiele razy pracowałem z legacy kodem, ale tutaj jest bardzo trudno, ponieważ nic nie wiemy o systemie oraz robi to jeden mid (ja 3.5 lata doświadczenia z lagacy kodem) i jeden junior developer. Technologie scriptlet / JSP / servlet wszystko na metodach statycznych, większość logiki na widokach lub w procedurach składowanych (kilkadziesiąt parametrów). Nie ma mowy o testach. Najpierw trzeba jakoś odtworzyć proces deployowania projektu. Jedna firma już na tym poległa.

Może uda się przekonać kierownictwo do Selenium, ale nie ma budżetu na takie rzeczy.

0

Chciałem odświeżyć trochę temat. Widzę kwestia TDD to kwestia dyskusyjna, jedni TDD stosują inni wręcz przeciwnie bo uważają to za stratę czasu itp.

mnie ten temat bardzo interesuje nadal i dlatego zakupiłem sobie książkę pt. "TDD Programowanie w Javie sterowane testami" Viktor Farcic i Alex Garcia i zastanawia mnie pewna kwestia wszędzie w sumie gdzie czytam o TDD napisali aby:

  • napisać test
  • odpalić test (wynik negatywny bo brak rozwiązania)
  • napisać rozwiązanie
  • odpalić test (wynik pozytywny)
  • refaktoryzacja kodu

a co w sytuacji kiedy projekt jest dosyć rozbudowany i same testy na przykład trwają po 5 min i więcej? domyślam się że to są niezbyt częste przypadki no ale na pewno się takie sytuacje zdarzają? w takiej sytuacji taki "szablon" na pewno nie przyśpiesza pisania kodu... czy się mylę?

0

Co do długich testów: raz, testy mają wykonywać się jak najkrócej, dwa, można odpalać tylko te, z którymi aktualnie pracujemy.

0

Według mnie TDD działa tylko jak większośc osób stosuje.

Samotny Rycerz Mocy TDD w projekcie z mojego doświadczenia zakopie się przy refaktoryzacji i braku co niektórych testów.

Teoretycznie z TDD i bez kod powinien być jakościowo podobny, ale ja takiego nie widziałem jeszcze.

0

no dobra ale w TDD nie chodzi głównie o testy ale TDD ma na celu chyba jeszcze porządkowanie w głowie samego projektu, podejścia do niego. Nie wiem może się mylę bo sam osobiście nie mam z tym doświadczenia.

0

a co w sytuacji kiedy projekt jest dosyć rozbudowany i same testy na przykład trwają po 5 min i więcej?

Nie musisz wtedy odpalać co chwila wszystkich testów w całym projekcie, a jedynie test dla twojego modułu, co najwyżej przed commitem odpalisz sobie wszystkie testy dla pewności.

Tyle, że projekt musi być dobrze napisany i moduły muszą być w miarę niezależne od siebie tak, żeby faktycznie to miało sens (z kolei w źle napisanej apce moduły mogą być za bardzo powiązane ze sobą i zmiana w jednym pobocznym module może wtedy rozwalić apkę w 20 miejscach - wtedy faktycznie byś musiał wszystko odpalać...)

0
rafal20-1988 napisał(a):

no dobra ale w TDD nie chodzi głównie o testy ale TDD ma na celu chyba jeszcze porządkowanie w głowie samego projektu, podejścia do niego. Nie wiem może się mylę bo sam osobiście nie mam z tym doświadczenia.

Można poniekąd stwierdzić, że testy to efekt uboczny całego podejścia do pisania aplikacji przy użyciu TDD. Głównym naciskiem ma być sam moment pisania testu. W ten sposób masz pewność, że później napisany kod, który przechodzi dany przypadek testowy robi dokładnie to co chcesz. Jest to sytuacja całkowicie przeciwna do pisania testu do istniejącego kodu. Wtedy praktycznie rzecz biorąc dostosowujesz test do kodu, a nie na odwrót - co de facto niewiele daje.

Jednakże tak jak już ktoś wspominał, jeśli tylko jedna osoba stosuje taką metodologię to zapewne nic z tego nie wyjdzie. Szczególnie jeśli zadania dodatkowo obejmują przykładowo naprawę bugów. Według mnie wtedy jest to nadmierna strata czasu, który mógłby być przeznaczony na coś innego.

Ogólnie to jak ze wszystkim - jeśli będziesz mądrze wykorzystywać metodologię to na pewno pomoże, jeśli będziesz ślepo podążać za wytycznymi to zapewne nie da żadnego zysku, a i może zaszkodzi.

2
rafal20-1988 napisał(a):

a co w sytuacji kiedy projekt jest dosyć rozbudowany i same testy na przykład trwają po 5 min i więcej?

To znaczy, że jest do d**y napisany projekt i TDD go nie uratuje. Moim zdaniem istnieją pewne wyznaczniki tego, kiedy TDD nie ma szans zadziałać w ramach istniejącego projektu:

  • długo wykonujące się testy – to znak, że muszą być odpalane wedle zasady "wszystko albo nic", moduły jak i poszczególne mniejsze elementy kodu są od siebie silne zależne, testy wymagają wielu przygotowań m.in. stanu aplikacji. Testy jednostkowe zmutowały do jednostkowo-integracyjnych.
  • "nierówne" pokrycie testami – niektóre części są dobrze pokryte, a inne słabo. Te pokryte słabo zapewne ciężko przetestować, bo są skomplikowane i wymagają wielu zależności.
  • nie wiadomo jak napisać test – najprawdopodobniej wymagania nie są poprawnie zdefiniowane i kod będzie miał za zadanie przerzucenie odpowiedzialności na inne elementy systemu.
  • Większość istniejących testów była pisana post mortem – najprawdopodobniej są niekompletne.
  • Ciężko wydzielić podzbiór testów do wykonania – zależności, zależności :)
0

@Koziołek: twierdzisz że testy nigdy nie będą się tak "długo" wykonywały? a co w sytuacji gdy aplikacja jest rozbudowana i samych testów jest 20k linii kodu?

2

@rafal20-1988: 20k linii kodu testowego to mało. W ELIXIRze mieliśmy około 35k linii testów (JUnity - około 6k testów) i całość nie wykonywała się dłużej niż 2 minuty na CI. Dlaczego? Ponieważ na 8 rdzeniowej maszynie mogłem puścić wszystko równolegle. Dlaczego? Ponieważ testy były prawdziwymi testami jednostkowymi, czyli m.in. nie korzystały z bazy danych czy plików, odseparowane, nie współdzieliły zasobów.

Do tego oczyliście były też testy integracyjne, które szły około godziny, ale musiały przeorać dość duże zbiory danych.

1 użytkowników online, w tym zalogowanych: 0, gości: 1