Písanie unit testov mám rád. Aj istotu, ktorú poskytujú, keď robím zmeny v neznámom kóde. Ale existujú situácie, keď mi unit testy zvyšujú tlak. Sú to problémy pri unit testoch, s ktorými sa stretávam bežne. Dúfam, že týmto článkom pomôžem k zníženiu ich početnosti.
Logovanie v testoch
Unit testy majú bežať úplne automatizovane a samé musia rozhodnúť, že niečo nie je v poriadku a primerane to oznámiť. Žiaden človek by nemal potrebovať skúmať logy či konzolu aby zistil, čo je problém.
Občas je testovaný komponent alebo koncept asynchrónny. Toto je bežná situácia, pri ktorej nájdem v teste logy o tom, ako ďaleko sa test dostal. Ale podľa mňa je to anti pattern a workaround ku poriadnemu unit testu.
Nevadí mi dočasné vypísanie nejakých informácií, keď sa snažím pochopiť zlyhávajúci test. Je to podobné ako použiť debugger. Aj v tom, že to je dočasné. Keď problém vyriešim, tak logovanie zmažem.
Veľa assertov bez popisu chýb
Bežne obsahujú testy viacero assertov. To je celkom OK, hoci ja preferujem väčšie množstvo testov s nízkym počtom assertov (ideálne jedným).
To čo mi vadí je, keď sekvencia assertov nijak neidentifikuje kontext zlyhania. T. j. popis zlyhaného testu nie je čitateľný. Programátor musí viac rozmýšľať čo sa stalo. A to aj v prípade, že klikne na stacktrace a hneď sa dostane na zlyhaný assert.
Autor testu by sa mal snažiť čo najviac zjednodušiť pochopenie dôvodu zlyhania. Keď uvedie v asserte aj message
, tak výrazne zjednoduší analýzu príčiny zlyhania.
Ja zvyčajne nepíšem message, ak je v teste len jeden assert a z mena testu vyplýva čo je problém.
Neprehľadné testy
Snažili ste sa niekedy opraviť zlyhávajúci test a trvalo to podstatne dlhšie ako urobiť zmenu v produkčnom kóde? Väčšinou je to spôsobené tým, že je test komplikovaný a neprehľadný.
Časom som si na takéto testy vypestoval alergickú reakciu. A v rámci prevencie sa snažím urobiť všetko pre to, aby som takéto testy nepísal.
Spôsobov ako vzniká neprehľadný test je obrovské množstvo. Na ich objasnenie by nestačil jeden článok, ale bolo by to na celú sériu článkov.
Testy čo testujú viacej konceptov naraz
Nie je žiaden dobrý dôvod, aby jeden unit test testoval viacej scenárov. Robia to buď začiatočníci, alebo tí, čo si chcú ušetriť prácu na príprave testovacieho scenára. Ušetria tak trochu času na napísanie testu. Ale nemyslia na všetkých budúcich čitateľov a tých nešťastníkov, ktorým ten test zlyhá. Tých svojou úsporou času odsúdili na dlhé zisťovanie prečo test zlyhal a ako ho opraviť. A to bez porozumenia testu, čo vlastne robí, nebude také ľahké.
Testy čo testujú príliš veľkú funkcionalitu alebo veľa komponentov naraz
Príprava scenára, kde je veľa spolupracujúcich komponentov si vyžaduje veľa kódu. Rovnako aj overenie, či všetko funguje ako má. Len zriedkavo nájdem takýto typ testu, ktorý je čitateľný aj napriek svojej zložitosti.
Komplikovaný test má aj viacej príležitostí zlyhať. Či už ho čítame, lebo zlyháva, alebo len chceme vedieť čo a ako robí, tak to nebude ľahká úloha.
Pomalé testy
Pomaly štartujúce alebo bežiace testy majú tendenciu prestať fungovať. Je to dôsledok toho, že ich programátori prestanú spúšťať.
Keď ich po čase niekto spustí zistí, že zlyhávajú. Takže musí zistiť dôvod. Či ich pokazil on, alebo už také boli. A keďže nemá čas riešiť problém, ktorý nespôsobil, môže ich buď zmazať/deaktivovať alebo…
Ak sa ich rozhodne opraviť, tak je skoro isté, že sa to za nejakú dobu zopakuje. Podľa mňa je oprava takých testov strata času. Mnohokrát bolo stratou času aj ich vytvorenie.
Testy asynchrónneho komponentu, ktoré čakajú N sekúnd či niečo nastalo
Špeciálnou formou pomalých testov sú aj unit testy, ktoré sa snažia otestovať asynchrónny komponent. Teraz mám na mysli komponent, pri ktorom nebol kladený dôraz na testovateľnosť. A tak je testovaný cez asynchrónne rozhranie. Takže test nemá šancu vedieť, či je operácia ukončená alebo nie. Štandardne sa to rieši čakaním. To prináša nasledujúce nevýhody:
- príliš krátke čakanie — dostávame falošné zlyhania, keď napríklad prebehne full GC a systém na chvíľu zastaví. Prípadne operačný systém začne swapovať pamäť. Bežným a nesprávnym riešením je predĺžiť čakanie. Lebo zlyhanie sa raz zopakuje. Alebo si povieme, že je OK ak občas tento test zlyhá. A potom si jeho zlyhania prestaneme všímať, a to je vhodná doba aby zlyhal z nejakého iného dôvodu.
- príliš dlhé čakanie — trpia všetci čo testy spúšťajú.
- pollovanie — toto by bolo dobré riešenie, lebo odstraňuje nevýhody predchádzajúcich riešení, ale zvyčajne trpí čitateľnosť testu.
- trpezlivo čaká na výsledok (napr. pollovaním alebo na notifikáciu). Bolo by to „dobré” riešenie, nebyť toho, že pri chybe hrozí, že výsledok nikdy nepríde.
V tomto článku nechcem zachádzať do detailov, ako to robiť lepšie. Asi o tom napíšem článok v budúcnosti. Len si musím pospomínať, ako som to robieval. V poslednom čase som to totiž nemusel riešiť.
Záver
Ani jeden z týchto problémov pri unit testoch nemusí existovať. Záleží len na nás — autoroch testov. Na odstránenie niektorých stačí chcieť. Na tie ďalšie treba zvyčajne lepšie nadizajnovať testovaný komponent.
„Vďaka mojim dlhoročným skúsenostiam s tvorbou unit testov a refaktoringom pomáham programátorom zmeniť ich postoj z musím na oveľa lepší — baví ma a dokážem.”