Tomáš Holý
24.4.2012

Unit testy a jazyk PL/SQL



Možná jste se již také na vašem projektu setkali s nutností vytvořit unit testy pro části kódu, které při načítání dat z databáze využívají uložené procedury nebo funkce. Toto na první pohled jednoduché zadání s sebou může ovšem nést jeden problém, a tím je použití java embedded databáze pro testování. Obecně mají embedded databáze slabou nebo dokonce žádnou podporu pro psaní uložených procedur a funkcí v jazyce PL/SQL, a proto je testování částí kódů, které je využívají, problematické. 

Na našem projektu Starthead používáme pro unit testy embedded databázi HSQLDB (Spring nám dává jednoduchou podporu pro využívání embedded databází HSQLDB, H2 nebo DERBY). Ovšem podpora jazyka PL/SQL je v této i dalších embedded databázi téměř nulová. Přesto jsme ale potřebovali nějak otestovat jednu část kódu používající uloženou funkci. Pokud se podíváme do dokumentace HSQLDB najdeme sice podporu procedur a funkcí v rámci SQL/PSM (Persistent Stored Modules), což nám v některých případech může stačit. Ve spoustě případů ovšem ne. A právě zde přichází na řadu podpora SQL/JRT, tedy SQL Routines and Types for the Java Programming Language. JRT nám dovoluje místo vytváření procedur a funkcí registrovat statické metody psané v jazyce Java, které poté budou volány na pozadí místo daných procedur, případně funkcí. Dost bylo nudné teorie, pojďme se podívat na jednoduchý příklad, který celý princip osvětlí. V aplikaci máme jednoduchou uloženou funkci, která například počítá průměr ze dvou čísel a vrací výsledek (berme to opravdu jen jako triviální případ pro vysvětlení principu). Naše funkce je psaná pro databázi MySQL.

CREATE FUNCTION average (a INT, b INT) RETURNS DOUBLE DETERMINISTIC BEGIN DECLARE sum DOUBLE; 
SET sum = (a + b) / 2;
RETURN sum;
END

 Nyní musíme tuto metodu převést do jazyka Java, aby poté mohla nahradit stejnou funkcionalitu v embedded databázi.

package com.aspectworks;
public class EmbeddedFunction{
  public static double average(int a, int b){ return (a + b) / 2; }
} 

Posledním krokem je registrace metody napsané v Javě pro volání funkce average v embbeded databázi. (Toto řešení je možné použít v HSQLDB 2.0 a vyžší. Nižší verze měli podporu aliasů, které fungovali na podobném principu.)

CREATE FUNCTION average (a INT, b INT) RETURNS CHAR DOUBLE LANGUAGE JAVA DETERMINISTIC NO SQL EXTERNAL NAME 'CLASSPATH:com.aspectworks.EmbeddedFunction.average' 

Pokud nyní v unit testech zavoláme sql dotaz, který používá tuto average funkci, na pozadí se provede vykonání metody average ve třídě EmbeddedFunction s danými parametry a jako výsledek se použije návratová hodnota metody. Jako návratová hodnota může být použita i tabulka, Java metoda poté musí mít návratový typ java.sql.ResultSet. Vnitřní chování převolávání takovéto metody se poté liší. Podrobně a srozumitelně je to popsáno v dokumentaci Java Language Routines (SQL/JRT). Nevýhodou tohoto řešení je nutnost udržovat společně s uloženou funkcí i metodu v dané třídě. Nechám už na posouzení každého, jestli je jednodušší použít toto řešení nebo pro testování použít některou z databází podporující jazyk PL/SQL. Uvítám vaše názory na tento způsob řešení, případně napište, jakým způsobem testujete uložené procedury a funkce vy. Pár odkazů: http://hsqldb.org – HSQLDB databáze http://en.wikipedia.org/wiki/SQL/JRT – popis JRT

Vaše emailová adresa nebude zveřejněna

Komentáře

Děkujeme za váš komentář
Další
  • wr

    Rekl bych, ze tohle reseni se da pouzit i v H2 databazi ... http://www.h2database.com/html/features.html#user_defined_functions

    1. Tomáš Holý

      Máte pravdu, toto řešení lze použít obecně v jakékoliv embedded databázi, která má podporu JRT (Routines and Types for the Java Programming Language).

  • Rob

    Podle mne to pekna pitomost, pokud pisete aplikaci zavislou na konkretni db, potom i testy by mely bezet nad touto DB, jinak tu logiku co mate v ul. procedurach neotestujete...

    1. Tomáš Holý

      Pitomost to opravdu není, může nastat případ, kdy vám nejde o logiku dané uložené funkce, ale naopak si můžete uloženou funkci nebo proceduru představit jako jakýsi MOCK objekt. Celý zbytek logiky, kterou máte v jazyce Java poté můžete jednoduše testovat oproti embedded databázi a nemusíte řešit žádné další režie spojení se správou databáze na každém stroji, kde chcete unit testy spouštět.