sobota 20. února 2010

Unit testy nad in-memory databází

V poslední době jsem několikrát narazil na otázku testování dao tříd a volby databáze, nad kterou testy běží. Překvapilo mě, jak kontroverzní otázka to je a jak vyhraněné názory na ni existují. Testujete dao třídy? Píšete unit testy pro cílovou databázi nebo používáte nějakou in-memory variantu jako HSQL? Mají testy s jinou než cílovou databází smysl? Na tyto a související otázky se pokusí odpovědět tento článek.

Testování dao tříd

Předně bych chtěl říct, že rozhodně jsem zastáncem psaní testů dao tříd. Ať už na projektu používáte ORM, přímý přístup přes JDBC nebo objektovou databázi, dao třídy obsahují množství kódu, jehož funkčnost je třeba ověřit. Správnost mapování, skládání dotazů, zpracování result setů, řešení krajních stavů nebo třeba odolnost vůči sql injection mohou být faktory, na které se typický test zaměří. Možnost použití jiné než cílové databázi (tou myslím databázi - Oracle, MySql, ... - která bude použitá v produkčním prostředí) zásadně závisí na tom, jestli se v kódu dao tříd vyskytuje konkrétní sql dialekt. Pokud se dotazujeme přímo přes JDBC a používáme pokročilé vlastnosti databáze, těžko můžeme dao třídy nechat běžet nad něčím jiným. Jaké jsou tedy důvody, které mohou někoho vést k psaní unit testů nad in-memory databází? 

Proč vůbec in-memory databázi používat?

Někdy mohou být zásadní náklady na vytvoření a správu testovacího prostředí. U běžně používaných databází (MySql, Oracle, PostgreSql, ...) většinou nepředstavuje další udržovaná instance velký problém. Existují ale výjimky. Setkal jsem se například s prostředím, kdy byl v produkci použitý Informix a kde vytvoření "další" instance nebylo snadné. Požadavek na novou instanci neznamenal konečnou, ale byl dost nákladný na to, aby ospravedlnil hledání jiných možností.

Dalším důvodem jsou (ne)závislosti, které unit testy potřebují ke svému běhu. V některých prostředích je výhodnější, když unit testy potřebují ke svému běhu jenom svůj a testovaný kód, nikoliv závislosti na dalších systémech včetně databáze. S tím úzce souvisí i opakovatelnost testů, resp. opakovatelnost očekávaného výsledku. Testy nad cílovou databází mohou být citlivější na změny a chyby v testovacích datech, pokud už data před testem obsahují.

Často diskutovaným důvodem je také rychlost spouštění testů. Zastánci testů nad in-memory databází někdy vyzdvihují jejich rychlost, kdy testy nad nimi běží údajně výrazně rychleji než nad cílovou databází. Já tento argument neuznávám. V případě in-memory databáze musíme vedle běhu samotného testu do výsledku započítat určitě vytvoření datového modelu, případně inicializaci dat a to jsou akce, které mohou něco stát.

Unit nebo integrační?

Jsou testy dao tříd nad databází unit testy nebo spíš testy integrační? Odpověď na tuto otázku nemusí ležet v kódu testů, ale v náhledu na ně. Pokud na test nad in-memory databází nahlížíme jako na unit test, můžeme in-memory databázi chápat jako stub, který prostě nahrazuje jednu závislost a dovoluje testu běžet v "izolaci". To je chápání, které je mi blízké. Pokud ale takový přístup nazveme integračním testem, nabízí se otázka jakou má takový test vypovídající hodnotu? O funkčnosti dao třídy toho může test hovořit hodně, ale o integraci s produkční databází velmi málo. Proto testy dao tříd nad in-memory databází za integrační nepovažuji.

Závěr

Jsem zastáncem testů nad in-memory databází tam, kde to má smysl. Dobrými důvody jsou pro mě náklady na správu testovacího prostředí a nezávislost unit testů na dalších systémech. 

Testujete na svých projektech dao třídy? Jakou databázi k testům používáte? Cítíte rozpor mezi použitím cílové databáze něbo nějaké in-memory varianty? Podělte se o své názory a zkušenosti v diskuzi pod článkem. Pokud přemýšlíte o tom, jak vytvořit a spravovat datový model pro testy, můžete se podívat na můj článek o nástroji LiquiBase. Pokud řešíte inicializaci dat pro testy, můžete některé odpovědi nalézt v článku o inicializaci databázových dat.