Category Archives: Tests

Unit testy a čistota návrhu

I když si myslím, že mám s unit testy dost zkušeností, stále ještě mě dokáží dost překvapit. Kromě toho, že se překvapuji tím jak často je „zapomínám“ psát, překvapují mě většinou pozitivně. Zrovna nedávno mě překvapily znovu.

Psal jsem program, který mimo jiné prohledává adresář na disku, poté nalezené soubory zpracovává, přesouvá jinam, archivuje, vytváří adresáře a dělá jiné psí kusy se souborovým systémem. Tuto část jsem chtěl otestovat. Ale jak na to? Jedno z dogmat unit testů nám říká, že unit test nepracuje se souborovým systémem. Kdybych použil třídu java.io.File, nemohl bych napsat unit test, protože tato třída na disk přistupuje. Řešení je jednoduché – musel jsem abstrahovat přístup na souborový systém. Přiznávám, že se mi do toho moc nechtělo. Abstrahovat přístup, který je už abstrahován jazykem mi přišlo zbytečné. Ale nakonec jsem se odhodlal a napsal jsem si rozhraní FileSystemDao



public interface FileSystemDao {
  
  public void copyFile(File source, File destinationthrows IOException;
  public void moveFile(File source, File destinationthrows IOException;
  public File[] listFiles(File directory, String pattern);
  public boolean isDirAccessible(File directory);
  public boolean isAccessible(File file);
  public File findOldest(File[] files);
  public boolean createDir(File file);
  public boolean delete(File file);
  public InputStream getInputStream(File filethrows IOException;
  public Reader getReader(File filethrows IOException;
}

Uznávám, že nazývat to DAO je trochu zavádějící, ale podle mě to vystihuje co jsem jím chtěl vyjádřit.

Skvělé, mám DAO pro přístup do souborového systému, mohu si vytvořit mock a vesele unit testovat. Nápad je to dobrý, nicméně má své vady na kráse. Výsledný test je neuvěřitelně nepřehledný. Vytvořit mock, který simuluje stav souborového systému před operací a po ní je docela náročné. Navíc jsem přišel na to, že tento test je až moc odtržen od reality. Neupozorní nás na to, že například kopírujeme soubor do neexistujícího adresáře. Při každé změně aplikace, se navíc testy musely dost pracně aktualizovat. Vydržel jsem to den a pak jsem tyto testy smazal. Kvůli pracnosti jsem zavrhl i myšlenku na to, že bych souborový systém simuloval v paměti, například pomocí nějakého stromu. Poslechl jsem Testuvia, který říká

Nezabředni do unit testového dogmatu

Napsal jsem normální funkční test, který využívá souborový systém. S tímto testem jsem nadmíru spokojen. Probíhá velmi rychle, hledá mi chyby a není s ním žádná práce. Navíc když se na něj podívám, krásně popisuje to co má testovaný kus kódu dělat.

Ale co se stalo s rozhraním FileSystemDao? Vypadá to, že teď už není potřeba. Opak je pravdou. Hodně se osvědčilo. Je v něm kód pracující se souborovým systémem soustředěný na jediném místě. Když jsem byl donucen změnit kód pro vyhledávání souborů pomocí masky, změnil jsem ho na jediném místě. Když se přišlo na to, že před každým přesouváním souboru musím vytvořit cílový adresář, zařídil jsem to změnou na jediném místě. Když se ukázalo, že je toto řešení neefektivní, opravil jsem to pouze v implementaci tohoto rozhraní.

Vidíme tedy, že mě potřeba napsat unit test donutila abstrahovat něco co by mě vůbec abstrahovat nenapadlo. Ono totiž na první pohled není o moc lepší psát fileSystemDao.moveFile(source, dest) než source.renameTo(dest). Teprve čas ukázal, že ta první, na první pohled nesmyslná varianta má své výhody. Nezbývá mi než si zopakovat starou pravdu

Unit testy si vynucují dobrý návrh.

Cesta Testiova

S vědomím toho, že zklamu své fanoušky, kteří jsou zvyklí na originální a vysoce vtipné příspěvky v mém blogu, dnes budu psát naprosto nepůvodně. Musím se s vámi podělit o nedávném archeologickém objevu. Sám jsem se o něm dozvěděl od pana Alberta Savoia.

Jde o objev vcelku velkolepý. Byly nalezeny nejstarší pozůstatky softwarového startupu. Mezi jinými artefakty byla nalezena knížečka s názvem „Cesta Testiova“. Uvedu jen výňatky, zbytek si sami určitě přečtěte v originálním překladu do angličiny.

Píšeš-li kód, piš i test
Žák se zeptal mistra programátora:
„Kdy mohu přestat psát testy?“
Mistr odpověděl:
„Až přestaneš psát kód.“
Žák se zeptal:
„Kdy přestanu psát kód?“
Mistr odpověděl:
„Až se staneš manažerem.“
Žák se zatřásl a zeptal:
„Kdy se stanu manažerem?“
Mistr odpověděl:
„Až přestaneš psát testy.“
Žák odběhl napsat nějaké testy.
Jen se za ním zakouřilo.

Pokud si kód zaslouží být napsán, zaslouží si mít testy.

Nejlepší čas na testy je, když je kód čerstvý
Tvůj kód je jako jíl. Když je čerstvý, je měkký a poddajný. Jak stárne, stává se tvrdým a křehkým.
Píšeš-li testy, když je kód čerstvý a je snadné ho změnit, testování je jednoduché. Kód i testy budou odolní.
Píšeš-li testy, když je kód starý a je těžké ho změnit, testování bude obtížné. Kód i testy budou křehcí.

Dobrý test selže
Žák přišel za mistrem programátorem a řekl:
„Všechny mé testy stále procházejí. Nezasloužím si povýšení?“
Mistr dal žákovi pohlavek a odpověděl:
„Pokud tvé testy stále procházejí, musíš psát lepší testy“
Žák s rudou tváří odběhl stěžovat si na HR.
Ale to už je jiný příběh