Blog | Je TDD mrtvé?

Je TDD mrtvé?

Možná jste zaznamenali rozruch, který vyvolal DHH tvrzením, že TDD v té podobě, jak je vnímáno (všechny závislosti třídy vymockované), je mrtvé. Reagoval Kent Beck, no a s ním snad stovky dalších lidí po celém internetu.

Jak vnímám TDD a jeho mrtvost já?

Tak především: TDD je vázané na unit testy. V okamžiku, kdy vývojář píše unit testy, může a nemusí TDD použít.

Tam se mi vyplatily 2 postupy:

  • pokud vím, co chci programovat, dělám pomocí TDD
  • pokud nevím a experimentuju, nastartuju interaktivní prostředí (tzv. REPL – Read Eval Print Loop) a vlastnost vyvinu, přiznávám se, kolikrát i bez testů a když se mi zdá, že je kód nestabilní, testy dopíšu dodatečně

Ale co považuju za nejdůležitější a o čem, mám pocit, Česká veřejnost vůbec nemluví, jsou další nástroje zajištění kvality. Jsou to nástroje, některé z nich už využívám, s jinými experimentuju, ale mám pocit, že jsem v Česku skoro jediný.

Design by contract

Každá funkce dostane mantinely pro vstupy a výstupy. Pokud se něco z toho vychýlí, strážci, kteří funkci hlídají, nahlásí chybu a v defaultní situaci aplikaci ukončí.

Stále častěji používám tyto postupy pro simulování statických typů, ale v situacích, ve kterých by si statické typování vylámalo zuby. Používám totiž často ad-hoc datové struktury a pro 5 kroků výpočtu mám i 5 netriviálních různých datových struktur, pro kterou vytvářet typ by bylo nepraktické. Přesněji vytvářet typ by praktické bylo, vytvářet pro to třídu (což je v mnoha jazycích jediná možnost, jak si přidat typ) by nebylo praktické.

Takže hlídám funkce a jejich korektní chování. Výsledek: posílíte úplně všechny své testy, zlepšíte stabilitu aplikace a testy jsou přímo součástí, což může znít divně (jako to má např. jazyk D nebo Eiffell), ale ve skutečnosti je to docela praktické.

Ad-hoc typy

S tím se dostáváme k dalšímu tématu. Tím je právě vytváření ad-hoc typů a kontrola běhu programu.

Píšu si custom SQL dotazy, ty putují do custom metod, dá se říci, že téměř žádné dvě metody modelu nevrací totéž. Principy, které dobře fungují u Doctrine, Nette\Database, by tady nemohly fungovat. Nemám žádné sjednocené entity, které se v celém zbytku aplikace tváří stejně.

V takové situaci, jak jsem zmiňoval už výše, používám kontroly těchto struktur třeba i v průběhu výpočtu. Na začátku cyklů/rekurzí/high order funkcí.

Toto je praktické hlavně v situaci, kdy mám vícekrokový výpočet a nevím, kdy přesně se výpočet rozbíjí. Mezi kroky vložím validační funkci, která buď předá data nezměněná, nebo vyhodí výjimku a takhle snadno přijdu na to, co se děje.

Výsledek: rychlejší hledání problémů se složitými datovými strukturami. Využití výhod statického programování uvnitř dynamického programování (psát typy jen tam, kde se mi to hodí a líbí).

Algebraické typy

Tento konstrukt mi učaroval, ale ještě jsem se nedostal k tomu ho použít v praxi. Funguje to následovně:

  • typ může být složen z variant, potom funkce, která něco vrací, může vrátit jeden z těchto typů
  • typy můžou mít něco jako dekorátory

Vás pak programovací jazyk nutí zareagovat na to, že funkce vrátila složený typ.

Příklad: downloadFromURL vrací složený typ String|Downlo­adFailed|UserA­borted a vy jako vývojář hned pod navrácenou hodnotou musíte ošetřit všechny 3 varianty. Tzn. je to trochu jako výjimka, ale je to čistě pomocí typů a když neošetříte situaci, program se ani nezkompiluje.

Podobně funguje i Maybe monáda, což je příklad druhé situace: funkce může vrátit hodnotu, ale občas to nedovede. Pokud se to nepovede, běžná funkce by vracela null. A null vede k NullPointerEx­ception, k jedné z výjimek, která mě po přechodu na JVM docela často trápí. Maybe monáda místo toho vrátí buď hodnotu (hodnota existuje) nebo typ Nothing, tzn. hodnota neexistuje, ale je to zakódováno v typu, ne v hodnotě. Výsledkem je, že programátor je nucen zareagovat a ošetřit, že funkce může vracet nejen hodnotu jednoho typu, ale i Nothing, což je další typ. Už při kompilaci dostáváte další úroveň ochrany.

V dynamickém jazyku budu muset toto simulovat pomocí datových struktur. Nevýhodou je, že se nedozvím o problému při kompilaci, ale při prvním spuštění. To mi až tak moc nevadí, protože přeci neposílám na produkci nikdy kód, který bych si nevyzkoušel nejdřív u sebe.

Existují i další mechanizmy, např. dependent types, tam můžete typy ověřit, že pole má např. sudý počet prvků, nebo že přijde pole, které má aspoň 3 prvky. O tom až v budoucnu, až s tím budu mít víc zkušeností.

Property-based testy

Věřím, že na algoritmy, paralelizované algoritmy, si multivariantní testování zatím hledá cestu ke slunci. Ale rozhodně k němu dojde. Sám už s ním trochu experimentuju a vypadá to na opravdu užitečný nástroj.

Jak to funguje:

  • místo dosazování hodnot do funkce, dosadíte generátory dat daného typu
  • kontrolujete, že se funkce pohybuje v nějakém daném intervalu (často nekontrolujete přímo návratovou hodnotu, ale jen to, že se vešla do intervalu nebo kontrolujete nějaké její vlastnosti – třeba že je výsledek seřazený)
  • spouštěč pustí testy třeba tisíckrát s daty, které si nageneroval
  • poté, co pro nějaké situace test neprojde, proběhme fáze, ve které hledá minimalistický příklad, ve kterém funkce selže (třeba generoval různá pole od 2 do 100 prvků a ty posílal, minimalistické pole má 2 prvky, kde 1. je větší, než 2.)

Tato forma testování je dobrým doplňkem k Unit Testům, jak vám asi dochází, nemůže je nahradit, ale v určitých situacích je vhodnější.

Simulační testování

Simulační testování dává odpověď, jak se bude systém chovat pod realistickou zátěží. Totiž, když vývojář zvyšuje výkonnost, je ve složité situaci. Pokud kliká na jednu stránku, OS i platforma si často něco zakešuje a kus kódu se začne chovat jinak, než se chová na produkci. Simulační test vytváří provoz, který připomíná ten skutečný. Pak nemá systém šanci nakešovat si jednu zlobivou stránku a vy se dozvíte o kompletní výkonnostní charakteristice.

Za normálních podmínek se vytváří poměrně složité modely a čas na vývoj takového řešení je netriviální. Jirka Fabián, když jsem mu o tomto testování povídal, vymyslel skvělou myšlenku. Na webovém serveru nahrávat requesty a pak pustit jen takový Re-Play. To je skvělá myšlenka. Jen zastínit osobní údaje, čísla kreditek a máte simulační test hotov.

V blízké budoucnosti si na toto napíšu middleware, nemělo by to být na víc, než 200, 300 řádek kódu.

A další

A pak tu máme ještě spoustu dalších forem testování, které už známé jsou, jako je akceptační testování, integrační a funkční testy.

Když se tohle všechno vezme dohromady, dostáváte do rukou obrovský vějíř možností a nástrojů. V takových podmínkách je někdy TDD nejvhodnější, jindy je nejlepší akceptační test, někdy obojí a jindy zas něco jiného.

Budu šťastný, když v ČR začnou lidé používat i další formy testování mimo Unit a Akceptačního (a občas integračního). Tím se celá diskuze může posunout zase o kus dopředu. TDD mrtvé není, je tu už dlouho a ještě tu dlouho bude, jen mu přibývají bráškové a sestřičky a vy byste se s nimi měli bavit taky.

Programování

Předejte zkušenosti i dalším a sdílejte tento článek!



Jiří Knesl
Business & IT konzultant

Jiří Knesl poprvé začal programovat v roce 1993. Od té doby, díky skvělým učitelům a později zákazníkům, měl možnost neustále růst v oboru vývoje webových aplikací a informačních systémů. v roce 2002 se přidal zájem o ekonomii a v roce 2006 o organizaci práce. Vším tím se konstantně profesně zabývá jak ve svém podnikání, tak i u zákazníků. Za posledních 5 let vydal na tato témata přes 400 článků.

Prohlédněte si moje reference

Mám zkušenosti z rozsáhlých projektů pro korporace, velké podniky, střední i malé firmy, ale i pro startupy v cloudu. Zvyšoval jsem jejich know-how, pomáhal nastavovat jejich organizační strukturu, byl lektorem a mentorem v náročných situacích. Podívejte se, jak vidí můj přínos samotní klienti.

Sledujte mé postřehy na sociálních sítích