Thursday, October 28, 2004

JDBC ve Birim Testler - HSQLDB

JDBC'yi direk kullanan ya da perde arkasında JDBC aracılığı ile bir şekilde veri tabanına bağlanan kodların birim testten geçirilmesi, bilgi işlemde dünyasında önemli hususlardan biridir.

Fakat, bu kodları test için hangi yöntemi takip edeceğiz? JDBC kodlarıyla işlettiğimiz SQL, ve bu SQL'dan ile gelen veri, çoğu bilgi işlem uygulamasının kalbini oluşturmaktadır. Eğer birim testleri için sadece JDBC arayüzlerinin taklidini koyarsak (ve bu taklitlerden betonlanmış boş veri geri döndürürsek), o zaman Java kodunun içindeki SQL komutlarının doğruluğunu test etmemiş olacağız. Hâlbuki bu kodların doğruluğu da işlem mantığının doğruluğu için çok önemlidir.

Zorlukları şöyle sıralayabiliriz:


* İşlem mantığı kodu, JDBC ile her noktada temas halindedir. Bu yüzden taklit etmek zordur.
* Birim test, eğer Oracle gibi okkalı, dısarıda çalışan bir veri tabanı kullanıyorsa, bu tabanın her zaman çalışıp çalışmayacağı garanti değildir, ya da programcının her zaman Oracle'ın olduğu yerde olması herhangi bir Oracle'a erişimi mümkün değildir.

Bu yüzden, daha önceki yazıda bahsettiğimiz taklit nesne kalıbını JDBC testleri için takip etmeyeceğiz. JDBC kodları için arayüzlerin konuştuğu nesneleri değil, veri tabanını tamamen değiştireceğiz.

Veri Tabanını Değiştirmek

Uygulamamız eğer Oracle kullanıyorsa, Oracle'in yerine "testler için" sadece hafızada çalışan pür Java bir veri tabanı koyabiliriz. Bu bellek veri tabanını çalıştırmak için tek lâzım olan tek bir JAR içinde olacaktır, ve ayrı bir süreç içinde çalışmasına gerek olmamalıdır, yâni admin gerekliliği sıfır olmalıdır. Bu test veri tabanı üzerinde tüm CREATE TABLE, INSERT komutlarını "testler" tarafından her lâzım olduğunda işletiriz.

Öte yandan, işlem mantığındaki JDBC kodlarının Connection nesnesini dışarıdan alması gerekecektir, böylece test sırasında işlem mantığı Oracle Connection'i ile değil, bu yeni "test veri tabanı" Connection'ı üzerinde işlem yapacaktır. Yâni test edilen işlem mantığı metotunu ateşlediğimiz zaman, çağrılan işlem mantığı dışarıda ne olduğundan habersiz, hep işlettiği kodları aynen çalıştırmaya devam edecektir.

Fakat arka planda, SQL komutları bizim sırf bu test için hazırlamış olduğumuz bellek veri tabanı üzerinde işletilecektir. Eğer kullandığımız SQL, ANSI standartına uygun ise, her taban üzerinde aynen işlemesi gerekir.

HSQLDB

Böyle bir veri tabanı Java için mevcuttur. HSQLDB veri tabanı, pür Java içinden kontrol edilebilen ve hafızada çalışan ANSI uyumlu bir veri tabanıdır. Kurmak için tek yapmanız gereken hsqldb.jar dosyasına CLASSPATH'e koymaktır!

Örnek olarak bir veri tabanı oluşturmak, tablo yaratmak ve veri koymak, şu şekilde yapılabilir.

public class BizimTest {

static String tabloSql =
"CREATE TABLE HESAP" +
"(" +
" kolon1 VARCHAR (12) NOT NULL" +
" , kolon2 decimal (10)" +
")" ;

static String testSatir1[] = {
"insert into hesap values ('111111111111',1)",
"insert into hesap values ('211111111111',1)",
"insert into hesap values ('311111111111',1)"
};

public void testVeriSatirlariniEkle(String arg[], Connection c) throws Exception{
for (int i=0;i<arg.length;i++){
PreparedStatement ps = c.prepareStatement(arg[i]);
ps.execute();
}
}

public void testTabaniOlustur(Connection c) throws Exception {
c.createStatement().execute("DROP TABLE HESAP IF EXISTS");
c.createStatement().execute(tabloSql);
c.commit();
}

Connection baglantiAl() throws Exception {
Class.forName("org.hsqldb.jdbcDriver");
return DriverManager.getConnection("jdbc:hsqldb:.", "sa", "");
}

...
...



Örnek veri koymak için gerekli olan işlevi testVeriSatirlariniEkle içinde görüyoruz.

Tabloları CREATE TABLE ile yaratmak için tablo yapısını gerçek (Oracle için yazılmış olan) CREATE TABLE komutlarından hareketle yazmamız gerekiyor. Kolon tiplerinin bazılarının değişmesi gerekebilir, meselâ Oracle NUMERIC tipini kullanırken, HSQLDB DECIMAL ya da DOUBLE tipini kullanmaktadır. Bu değişimi otomatik olarak yapacak bir Perl betiği aşağıda verilmiştir.

open IN, $ARGV[0];
undef $/;
open OUT, ">hsqldb_tablo_yarat.sql";
$_ = <IN>;

s/BLOB/VARCHAR/sg;
s/CREATE TABLE (.*?)\n\(/DROP TABLE $1 IF EXISTS\;\nCREATE TABLE $1\n\(/sg;
s/(.*?)(\s*?)NUMBER(\s*?)\((.*?)\)/$1$2DECIMAL$3\($4\)/sg;
s/(\s*?)NUMBER(\s*?)/$1DECIMAL$2/sg;
s/(\s*?)RAW(\s*?)/$1VARCHAR$2/sg;
s/(.*?)(\s*?)VARCHAR2(\s*?)\((.*?)\)/$1$2VARCHAR$3\($4\)/sg;

print OUT;
close IN;
close OUT;


Şimdi bir test yazalım:

        ...
public void testKacHesapVar() throws Exception {

testTabaniOlustur(c);
Connection c = baglantiAl();
testVeriSatirlariniEkle(testSatir1, c);

IslemMantigi mantik = new IslemMantigi(c)

ResultSet rs ;

rs = mantik.kacKisiVar();

int kisiSayisi = 0;
while (rs.next()) kisiSayisi++;
assertTrue(kisiSayisi == 2);
}

} // BizimTest sonu


Şimdi, IslemMantigi nesnesi içinde test edilen kacKisiVar() metodunun gerçekleştirimini görelim.

public class IslemMantigi {

protected Connection baglanti = null;

public IslemMantigi() {
try {
Class.forName("jdbc:oracle:oci:@TEST");
baglanti = DriverManager.getConnection("oracle.jdbc.driver.OracleDriver".
"kullanici", "sifre")
} catch (Exception ex) {
log.error("",ex);
}
}

public IslemMantigi(Connection c) {
baglanti = c;
}


public int kacKisiVar() {

int kac = 0;

try {
PreparedStatement ps2 =
queryConnection.prepareStatement("SELECT COUNT(*) FROM HESAP");
ps2.setInt(1, 1);
ResultSet rs = ps2.executeQuery();
while (rs.next()) {
kac = rs.getInt(1);
}
rs.close();
} catch (SQLException ex) {
System.err.println("" + ex);
}

return kac;
}
...
}

Dikkat edelim: IslemMantigi nesnesinde bir değil, iki tane kurucu metot (constructor) var. Bunun sebebi, daha önce bahsettiğimiz veri tabanı bağlantısını testler için dışarıdan verme ihtiyacından ileri gelmektedir. Boş kurucu metodun bağlantıyı Oracle'a giderek kendi kendine aldığını görüyoruz, testlerin de IslemMantigi nesnesini HSQLDB'ye işaret eden bir Connection ile yaratabileceğini görüyoruz. Nesnesinin geri kalan kısmının tüm bu taklalardan hiçbir haber olmamaktadır. Bu bölümler için zaten bir Connection vardır, ve PreparedStatement, ResultSet ile gereken işlemler yapılır.

Kaynaklar


* HSQLDB, jMock, ve diğer bazı test etme tekniklerini anlatan bir sunum
* HSQLDB
* JDBC Kodlarının Tasarımı ve Testi (Design and Test JDBC Code)

No comments: