Test-Driven Development (ďalej len TDD) opísaný v predchádzajúcom článku je na prvý pohľad zvláštny proces, priečiaci sa zdravému rozumu. Veď ako môžeme najprv napísať test a až potom implementáciu? Jednoducho. Presne tak isto, ako môžeme najprv stanoviť, čo chceme naprogramovať a až potom to urobiť.
Práve táto požiadavka, spolu s postupným striedaním písania testu a implementácie je ten dôvod prečo to funguje a prináša toľko výhod. Poďme sa na ne pozrieť podrobnejšie.
ČO komponent robí vs. AKO to robí
Dopredu napísaný test kladie dôraz na to, čo komponent robí, nie na to, ako to má dosiahnuť. Takýto test je zvyčajne čitateľnejší. Čitateľnosti taktiež pomáha, ak je test krátky a testuje, čo najmenej.
Je pomerne bežné, že test napísaný „až po” presne kopíruje produkčný kód, t.j. je plný ako. Čitateľnosť takých testov je nižšia.
Test slúži ako špecifikácia
Keď potrebujeme do testu zapísať, čo komponent robí, tak musíme rozumieť špecifikácii komponentu.
Samotný test by mal byť jasný a krátky. Jeho meno by malo zodpovedať tomu, čo sa v ňom deje — popisovať, aký scenár testuje. Takto test funguje ako špecifikácia. Vykonateľná, a teda vždy aktuálna.
Naopak, pri tradičnom prístupe sú bežné názvy testov ako testCalculationThrowsException. Z názvu nie je jasné, prečo by mal komponent vyhodiť výnimku. Ak to potrebujeme zistiť, musíme preskúmať test alebo aj produkčný kód.
Testy sú rýchle
TDD negarantuje rýchle testy, ale ak chceme pracovať podľa princípov TDD, tak sa musíme snažiť udržať testy dostatočne rýchle. Inak nás pomalý cyklus začne frustrovať a začneme hľadať skratky. Prestaneme spúšťať testy pred implementáciou komponentu, prípadne spravíme viac cyklov a testy spustíme len raz na konci. Môžeme to tak robiť, ale prídeme o výhody TDD.
Ak neviete dosiahnuť pri nejakom komponente primeranú rýchlosť, tak skúste oddeliť pomalé testy od ostatných a komponent urobiť bez TDD.
Komponent bude dobre nadizajnovaný
Keď píšeme testy až po ukončení vývoja komponentu, tak veľmi často zistíme, že komponent nie je ľahko testovateľný. Buď ho prerobíme, alebo budú testy komplikované a veľké. Prípadne žiadne.
Pri TDD si neviem predstaviť, ako by sa dal napísať produkčný kód, tak aby nebol testovateľný, keďže jeho test existuje skôr.
Testovateľnosť vo svojej podstate znamená, že vieme zobrať komponent a použiť ho nezávisle od kontextu, v ktorom má byť používaný. V našom prípade je to unit test. Ale rovnako ho môžeme zobrať a znovu-použiť v nejakom inom kontexte, o ktorom pôvodný autor ani netušil.
Menej chýb dnes a aj v budúcnosti
Testovateľnosť má vplyv na pokrytie kódu testami. Takto TDD umožňuje dosiahnuť vyššie pokrytie, ktoré chráni pred budúcimi chybami.
Taktiež klesá riziko, že autor testu doň zavedie chybu z produkčného kódu. Toto sa môže ľahko stať ak je produkčný kód napísaný skôr a autor sa ním „inšpiruje” pri písaní testov.
Pomáha vytvoriť ideálne API
Test je prvý klient vyvíjaného komponentu, t.j. vďaka testu musíme rozmýšľať o tom ako sa bude komponent používať ešte skôr ako rozmýšľame o tom, ako dosiahnuť požadovanú funkcionalitu. Keďže testy a komponent vznikajú postupne, tak aj výsledné API máva iný stupeň granularity ako pri tradičnom prístupe.
Okrem toho, TDD pomáha rozbiť riešený problém na podproblémy (riešené inými komponentami) a definovať pre ne API. Viac sa môžete dozvedieť v tomto článku.
Nenastáva syndróm „Veď mi to už funguje”
Keď pomocou TDD vytvoríme test, tak produkčný kód ešte neexistuje. Takže nám nehrozí riziko typické pre písanie testu až po manuálnom otestovaní komponentu. Tým je pocit, že všetko funguje ako má, som skvelý a už len rýchlo dokončiť test(y) a môžem začať robiť na niečom zaujímavejšom.
Takýto pocit nás presvedčí, že odfláknutý test je lepší ako žiadny, najmä, ak tie testy robíme skôr preto, že ich manažment od nás očakáva. Nejaký test napíšem, pridám aj jeden-dva asserty, napríklad assertNotNull na návratovú hodnotu a som hotový.
Test naozaj testuje to, čo má
Požiadavka vytvoriť zlyhávajúci test pred naprogramovaním produkčného kódu je úžasný spôsob ako zabezpečiť, že test neobsahuje chyby.
Keď programujeme testy ku existujúcej funkcionalite, zvyčajne sme radi, keď to celé spustíme a test je zelený. Hurá, ešte tri takéto testy a som hotový. Už som aj tak ráno oznámil na daily standup-e, že úloha je hotová, len dokončím unit testy.
Ale je to naozaj tak? Ak sme nevideli ako tento test dokáže zachytiť nejakú chybu, kde berieme tú istotu, že to naozaj dokáže? Čo ak máme v teste len jednoduchý assertNotNull na návratovú hodnotu, ale metóda vracia nezmysly?
Záver
Skúste sa zamyslieť nad tým, koľko z týchto výhod vám pri písaní testov až po naprogramovaní komponentu chýba. A vôbec, či ich považujete za výhody alebo nie. Ja a mnohí iní programátori áno.
Potrebujete praktickú ukážku ako TDD funguje v praxi? Prihláste sa na toto školenie.
Ďalšie Pavlove články na túto tému:
- Dokáže Test-Driven Development vyriešiť môj problém?
- 8 dôvodov, prečo je TDD príjemný spôsob vývoja softvéru
- Nie je Test-Driven Development príliš pomalý spôsob vývoja softvéru?