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.

4 komentáře:

  1. Zcela jistě testování DAO má smysl. In memory databáze mají smysl, pokud kód používá nějakou abstrakci nad JDBC, která jej odstíní od vlastních dialektů (Hibernate). Řekl bych, že kromě nejjednoduššího použití, neexistuje přenositelné SQL. Osobně píši testy nad cílovou databází s použitím Transaction rollback on teardown patternu (Spring). Bez něj by byla doba běhu DAO testů neúnosná. Teď jsem si měl šanci vyzkoušet pro svůj nadcházející workshop psaní testů nad in memory (HSQL) databází a musím říct, že rychlost je opravdu skvělá - kompletní drop a recreate database i s testovacími daty je otázka milisekund.

    OdpovědětVymazat
  2. Na tyhle tety muzu doporucit H2 databazy. Ma uzasnou rychlost a zvlada ruzne SQL dialekty (Mysql, Oracle...)

    A nedoporucuju moc resit rozdil mezi unit / integracnimi testy. Potrebne jsou oba. Udelejte dva ruzne test suite, jeden rychly s minimem zavislosti, druhy integracni ktery muze bezet klidne nekolik hodin.

    OdpovědětVymazat
  3. pouzivame hibernate a na testy h2. rychlost velka, dostatocna admin konzola, ziadna udrzba. velka spokojnost.

    OdpovědětVymazat
  4. Testovat DAO? Rozhodně ano. Jak už tady zaznělo, klasifikace testování na unit a integrační testy je obecně nejednoznačná. Závisí na vlastnostech sytému a prostředí - technologie, komplexnost, distribuce, přenositelnost, ... Z mojí preference nezávislosti aplikace na cílové databázi pramení přesvědčení, že testování DAO jsou unit testy. Testování nad cílovou databází je potom logicky integrační.
    Používám Derby, resp. její Sunovský klon JavaDB od té doby, co se stala součástí JDK. Nejednalo se sice o regulerní in-memory databázi, ale vyhovovala mi jednoduchostí použití, rychlost nebyla důležitá. A od verze 10.5 (myslím) už podporuje plnohodnotnou in-memory instanci.

    OdpovědětVymazat