Databázové scénáře pro integrační a jednotkové (unit) testy
Pokud máte aplikaci, která je výhradně závislá na databázi, a chcete psát jednotkové
a nebo integrační testy, pak jste postaveni před dva problémy.
- nastavení testovacích dat alias uvedení databáze do výchozího stavu
- kontrola dat v databázi v rámci testu
Jednou z možností, jak se výše uvedených problémů zbavit, je použít techniku
tzv. mockování. Technika mockování je založena na tom, že vlastní testovaný
objekt nepracuje v runtime s instancemi závislých objektů, ale s jejich atrapami,
které v sobě mají předpřipravená data.
Ač jsou mock objekty dobrým řešením, především pro jednotkové testy, mají svá omezení. Tím nejzásadnějším, alespoň z mého pohledu, je nemožnost simulovat chování komplexních procesů, které probíhají na pozadí. Typickým příkladem pro databáze je omezení referenční integritou. Je velice neefektivní si připravovat mock objekt, který bude věrně simulovat chování databáze. Samozřejmě to nemusí být přímo databáze, ale například aplikační mezivrstva v podobě Hibernate a pod ní teprve databáze.
Další alternativou k mock objektům, a tentokrát plnohodnotnou, představuje framework
DbUnit. DbUnit umožňuje
jednak provést nastavení testovacích dat a také jejich kontrolu. Práci s DbUnit
ukazuje článek Andrew Glovera Effective Unit Trstiny with DbUnit.
DbUnit je velice užitečný nástroj a má API podporu pro nejrůznější operace
přes porovnávání datasetu až po extrakci databázových schémat. Jenže,
starého psa, novým kouskům nenaučíš.
Jde o to, že jsem:
- nechtěl nutit vývojáře vytvářet testovací data jako XML
- nechtěl nutit vývojáře zkoumat další API
- chtěl umožnit jednoduché využívání/rozšiřování předpřipravených dat
Výsledkem je jednoduchý “framework”, který jsem pojmenoval ScenarioTestCase, a který
je založený na jUnit. Stručná charakteristika by mohla znít takto: slouží pro psaní integračních
a
jednotkových testů za použití standardního API Javy a klasického SQL, s cílem umožnit jednoduché znovupoužití/rozšíření existujících testovacích dat.

Plná velikost (cca. 38KB)
Srdcem celého díla je abstraktní třída ScenarioTestCase, která jednak řídí životní cyklus testů a umožňuje spouštění testovacích scénářů. Termín testovací scénář, kterému odpovídá rozhraní TestSQLScenario, představuje sadu SQL dotazů starajících se o inicializaci a úklid testovacích dat.
ScenarioTestCase zaručuje:
- před spuštěním testu volání metody
onStartUp - před každou testovací metodou exekuci všech zaregistrovaných scénářů
- před každou testovací metodou volání
onSetUp - po každé testovací metodou volání
onTearDown - možnost vykonat libovolný SQL dotaz (insert, update, delete, select)
Za zmínku stojí ještě třída ConnectionscenarioTestCase, která je potomkem ScenarioTestCase. Ta umožňuje otevření databázového připojení na základě předaných parametrů a dále implementuje práci s JDBC. Celý princip psaní testu ukazuje následující příklad. Modelová situace, máme tabulku, ve které je uloženo přihlašovací jméno uživatele a nějaké další hodnoty. Výsledný testovací objekt se pak snaží ověřit funkčnost aplikační logiky, která by měla změnit přihlašovací jméno
Napsání scénáře
Testovací scénář obsahuje SQL příkazy pro inicializaci a úklid databáze. Každý testovací scénář musí implementovat již zmíněné rozhraní TestSQLScenario, které definuje dvě metody. Metoda getInitSQL slouží pro vrácení seznamu inicializačních SQL příkazů. Metoda getCleanUpSQL slouží pro vrácení úklidových příkazů.
public class ExampleScenario implements TestSQLScenario{
public List getInitSQL() {
ArrayList l = new ArrayList();
l.add("insert into users (uname, user_id,) values ('pavel', 1)");
return l;
}public List getCleanUpSQL() {
ArrayList l = new ArrayList();
l.add("delete from users where user_id = 1979");
return l;
}
}Vytvoření vlastního testu
Vlastní testovací objekt je napsán jako potomek třídyConnectionscenarioTestCase. V metoděonStartUpse provede vytvoření testovacího scénáře a jeho registrace pomocí metodyaddScenario. Takto je samozřejmě možné registrovat libovolné množství scénářů. TřídaConnectionscenarioTestCasedefinuje abstraktní metodu
getConnectionParametrs, pomoci které potomci specifikují parametry pro otevření databázového připojení.
public class ExampleTest extends ConnectionScenarioTestCase {
protected String[] getConnectionParameters() {
return new String[]{
"com.mysql.jdbc.Driver",
"jdbc:mysql://localhost/test",
"test",
"test"};
}protected void onStartUp() {
super.onStartUp();
addScenario(new ExampleScenario());//přidání scénáře
}public void testFoo(){
//zinstancování aplikační logiky
AplikacniLogika al = new AplikacniLogika();
//změna uživatelského jména
bl.zmen('šavel')
//Pro ověření použijeme select, kde předpokládanou změnu
//zahrneme v rámci where klauzule viz uname='šavel'.
int count =
jdbcSelect("select count(*) from users where uname='šavel' and user_id = 1");
//V případě, že logika zafungovala správně, musí byt v proměnné count hodnota 1
assertEquals("Uživatelské jméno musí být změněno", 1, count);
}
public void testHoo(){
//test something else
}
}Testovací objekt lze pouštět jako jakýkoliv jiný jUnit test.
Rozšiřitelnost
ScenarioTestCaseje určen k tomu, aby byl rozšířen patřičným způsobem aConnectionScenarioTestCasepředstavuje ukázku, jak to lze udělat. Typické
rozšíření může například představovat implementaci, kdy nastartujeme například
databázový pool a nebo kontejner (Spring), ve kterém "žije" aplikační logika.
Objekty implementující rozhraníTestSQLScenariolze skládat a
nebo rozšiřovat. Myšlenka je taková, že by mělo vzniknout několik základních scénářů,
které se využívají přes většinu testů. Pokud je potřeba testovací scénář rozšířit,
udělá se jeho potomek a nebo kompozice.
Tento jednoduchý "framework" není rozhodně zamýšlen jako konkurence nebo náhrada DbUnit. Je to pouze jednoduchoučká alternativa pro pohodlné psaní testů závislých na databázi. Zdrojové soubory a zkomipolované třídy je možné použít dle libovůle. Jenom připomínám, že je potřeba mít na classpat jUnit a že je to psané pro Javu 5.0.
Související články
Související epizody CZ Podcastu
- #261 · Leadership
O vedení lidí a týmů.
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi a Filemon o novinkách v Javě 6 a webových frameworcích.
- #1 · Vývojová prostředí v Javě
První díl CZ Podcastu se věnuje vývojovým prostředím pro Javu.