Michal Dančák
Senior Test Engineer
Blog
Michal Dančák
Senior Test Engineer
Automatizace testů už dávno přestala být vzácností. Stále více projektů automatizované testy nějakým způsobem zavádí a testování databází v tomto ohledu není výjimkou. V rámci následujících řádků si představíme, jak na databázové testy a jakým způsobem lze co zautomatizovat. Vycházet přitom budu z cca tří let zkušeností s databázovými testy v bankovním prostředí. Čtěte dál, pokud vás zajímá:
Data vládnou světu. V dnešní době je informace to nejcennější, co existuje, a tyto informace se ukládají v nějaké formě do databází. Nejrozšířenější jsou databáze relační (RDBMS), kde data jsou uložena v několika (stech) tabulkách, které mají mezi sebou různé vztahy. Data v takovýchto databázích lze spravovat pomocí dotazovacích jazyků.
V relačních databázích se jedná o jazyk SQL (Structured Query Language). Každý navrhovatel řešení má přitom vlastní mutaci tohoto jazyka – Oracle SQL je o trochu jiný než PostgreSQL nebo MSSQL, ale všechny tyto jazyky fungují na společném základě. Každý jazyk má navíc programovací nadstavbu, která umožňuje použít některé programovací techniky jako větvení, smyčky či definování funkcí a procedur. Pro Oracle je takovým jazykem PL/SQL, pro Postgre to je PL/pgSQL, pro Microsoft pak T-SQL. Velké firmy pak využívají Big Data řešení, o kterých si ale povíme jindy.
Nejčastějším případem, se kterým se na projektu setkáte, jsou databáze, které neslouží pouze jako skladiště dat, ale data v ní jsou živá a přenášejí se mezi jednotlivými systémy. Každý systém má přitom vlastní oddělenou databázi. Tomuto procesu se často říká ETL (případně ELT), kdy na nějaké pravidelné bázi nahrajeme do databáze data, nejčastěji formou flat files, která přichází z různých systémů firmy.
Tato data se nejdřív očistí a sjednotí do jedné formy, poté se zpracují a eventuálně nahrají opět do souborů, které se odešlou do dalších systémů. Většinou se přitom striktně odlišují jednotlivé vrstvy v databázi, pro něž se používají odlišná schémata. Základní dělení je na vstupní, výpočetní a výstupní vrstvu. Mohou ale existovat i další. Celý přenos se přitom děje zcela automaticky. Každý z těchto jednotlivých kroků lze testovat. Důležité jsou přitom i end to end testy, kdy se testuje celý koloběh se zapojením okolních systémů. Nemělo by se zapomínat ani na testy správné historizace záznamů, performance testy a případně integrační testy, má-li databáze frontendový přístup. My se dnes zaměříme na testy správného přenosu a výpočtu dat přímo uvnitř databáze, kdy se data z jedné vrstvy přenesou do té následující, a přitom se můžou, ale nemusí nějakým způsobem transformovat.
Hlavním vstupem pro nás při testování databází bývá zadávací dokument pocházející od businessových nebo technických analytiků. V tomto dokumentu je popsaný vztah mezi jednotlivými tabulkami a sloupci. Správná implementace těchto vztahů je zcela klíčová pro fungování databáze a na ně je také rozumné se zaměřit. Zde je také často možné zapojit i automatizaci testů. Obecně lze nalézt několik úrovní, na nichž lze automatizovat. Většinou nám zde pomůžou programovací nadstavby obyčejného SQL.
V závislosti na projektu je možné, že některé z těchto úrovní nelze zavést. Často lze ale použít alespoň nějaké poloautomatické řešení s více či méně častými ručními zásahy.
Automatická tvorba testů je většinou největším oříškem pro automatizaci databázových testů, v závislosti na frekvenci změn a velikosti projektu ale může ušetřit nejvíc času, a tedy i peněz. Bývá zde také nutná součinnost analytiků, kteří nám dodávají zadání.
Nejčastěji získáme zadání formou excelové tabulky, kde jsou popsané jednotlivé přenosy dat. Typický řádek může specifikovat různé sloupce ve vstupní vrstvě, technicky či slovně popsanou transformaci a cílový sloupec ve výpočetní vrstvě. Součástí zadání také musí být popsané vztahy mezi všemi těmito tabulkami. Celý takový list Excelu lze poté naimportovat do databáze někam do pracovního testovacího schématu. Generátor poté tyto tabulky projde, vyhledá změny oproti předchozí verzi a pro každou změnu vygeneruje nové testy nebo updatuje staré. Bohužel tím, že zadání je nejčastěji psáno ručně, objevují se v něm překlepy, odlišnosti od zavedených postupů či drobné chyby. Tyto rozdíly lze většinou najít a opravit ručně poměrně snadno, ale automat si s nimi neví rady. K chybám a překlepům typicky patří tyto příklady:
V takovýchto případech je nutno zvážit, nakolik se tento problém může opakovat a jak moc velké úsilí musíme vložit do implementace nějaké výjimky v našem generátoru. Odlišnosti ve formátování se většinou vyplatí ošetřit, překlepy ne.
Výstupem generátoru pak můžou být nové záznamy v tabulce testů, kde jeden ze sloupců bude obsahovat kompletní text testu (tj. spustitelný dotaz v jazyce SQL). Tyto následně můžeme exportovat do fyzických souborů na disku nebo spouštět přímo z databáze, například použitím jednoduché smyčky, která bude každý test spouštět příkazem EXECUTE IMMEDIATE nebo jeho ekvivalentem.
Chytré vytvoření testů nám usnadní jejich automatickou exekuci. Kromě samotného spuštění chceme získat i nějaký záznam o testu a o jeho výsledku. Můžeme opět jednoduše zaznamenávat do výsledkové tabulky přímo v databázi. O reporting se většinou postará už generátor. Jestliže místo dotazu SELECT provedeme INSERT, můžeme výsledek testu zapsat do tabulky. Je přitom většinou nutné sjednotit výsledky testů. Ty by měly mít jednotnou formu, například záznam o počtu rozdílných řádků, případně příklad nějakého takového řádku. Nesmí samozřejmě chybět unikátní identifikátor testu, časový záznam a další typické náležitosti. Celou exekuci je možné nastavit na konkrétní čas například pomocí nástroje Jenkins, nicméně zde již výrazně rostou problémy s připojením k databázi, přístupovými právy a podobně.
Po exekuci všech testů lze ověřit výsledky. V typickém případě je možné obecně definovat, zdali test uspěl nebo selhal. Máme-li zkontrolovat, že sloupec AMOUNT se jedna ku jedné přesouvá z tabulky A do tabulky B, které se párují přes ACCOUNT_ID, stačí si testem vypsat všechny řádky, kde A.AMOUNT<>B.AMOUNT a pokud žádné neexistují, test uspěl. V opačném případě je potřeba začít analyzovat rozdíly. Některé lze považovat za PASS, například rozdíl v defaultní hodnotě. To už záleží na konkrétním projektu. Analýzu incidentů je prakticky nemožné zautomatizovat – důvodů pro rozdíly v hodnotách A.AMOUNT a B.AMOUNT může být nesčetně.
V současnosti v trojčlenném týmu testujeme Oracle datový sklad pro mezinárodního klienta v oboru bankovnictví. Každý týden dostaneme novou dodávku od analytiků ve dvojí formě – část jako .sql soubory, kterými si vytvoříme pomocné tabulky (rozhraní) a část jako excel tabulky popisující veškeré mapování jednak mezi Base a Stage vrstvami a jednak mezi Stage a Stage. Na tvorbu pomocných tabulek máme spouštěcí soubor. Ten nám postupně automaticky volá jednotlivé CREATE TABLE skripty ve správném pořadí a následně pro každou tabulku provede základní smoke testy – jestli se tabulku vůbec podařilo vytvořit a jestli má stejný počet záznamů, jako její vývojářský obraz.
Zatímco se nám tabulky tvoří, nahrajeme excely do databáze a spustíme na ně generátor testů. V excel tabulkách máme sloupec s informací o poslední změně. Generátor filtruje pouze ty záznamy, které jsou pro něj nové. Pro každý takový nový záznam vyčte, které cílové tabulky se týká a pro každý jednotlivý sloupec v té tabulce ze zadání zjistí, jak má být naplněn. Zbylé testy netřeba přegenerovávat. Stejně tak si získá i informaci o párování všech tabulek, které do testu vstupují. Vygenerování jednoho testu poté trvá řádově jednotky vteřin, následná kontrola testu může trvat cokoliv mezi jednou minutou a jednou hodinou v závislosti na objemu ručních úprav.
Tak jako pro pomocné tabulky máme i pro testy mapování několik exekučních souborů. Všechny testy dohromady běží cca 12 hodin, ale lze je nechat puštěné na pozadí nebo přes noc. Následně ručně zkontrolujeme výsledky. Úspěšné testy je možné zkontrolovat během chvilky, ty neúspěšné vyžadují hlubší analýzu. Díky rychlé kontrole je možné zahrnout celou testovací sadu do regresních testů. Během jednoho týdne tedy otestujeme zhruba 10 nových požadavků a 300 regresních testů. Z nich se přibližně 100 týká pomocných tabulek a 200 mapování. Exekuci a kontrolu má na starosti jeden člověk, který je tedy díky automatizaci schopen spustit 300 testů týdně. Stále přitom máme časovou rezervu pro několik set dalších testů, lze-li předpokládat, že většina z nich uspěje.
Na projektu používáme dva generátory testů – jeden pro mapování a druhý pro pomocné tabulky. Mapovací generátor je psaný v SQL a SQL*Plus, generátor testů rozhraní je o něco novější a je psaný v PL/SQL. Vygenerované testy máme uložené v databázi i na disku. Spustit je je možné z obou míst podle potřeby, nicméně vzhledem k tomu, že testy často mívají tisíce až desítky tisíc znaků, je ruční úpravu testů mnohem pohodlnější dělat na disku, odkud tedy testy většinou i spouštíme. Oba generátory jsme vyvinuli přímo pro projekt. První verze byla připravená za týden, updaty trvají řádově hodiny a provádíme je v průměru jednou za dva měsíce.