yazılım,c# ve domates kabuğu
« nightshiftDaha iyi program nasıl yazılır ? »

Unit Test

  22/02/07 12:08, by ertan, Categories: Yazılım

Yazdığınız bir fonksiyonun herzaman için düzgün çalıştığından nasıl emin olursunuz ?

Gündelik

Örneğin

function Musteri MusteriGetir(int musteriNo);

şeklinde verdiğiniz bir müşteri numarası ile veritabanından müşteri bilgilerini okuyup bunu bir class olarak çeviren bir fonksiyonunuz var. (Burda örnekler çoğaltılabilir, örneğin bir sql cümlesini çağıran fonksiyon yada telefon numarasını düzgün girilip girilmediğini kontrol eden bir fonksiyon, ne yaptığı çok önemli değil)

Alışıldık programlamada fonksiyon geliştirilirken bir kaç test sonucunda fonksiyonun müşteri numarası verildiğinde doğru bilgileri çevirdiği kontrol edildikten sonra diğer fonksiyonlara geçilir. Yine alışıldık olarak fonksiyonun ilerde de doğru çalışacağı varsayılır.

Buraya kadar hava durumu güneşli bir nisan günüdür.

Uygulama üzerinde zaman içerisinde değişiklikler yapılmaya devam edildikçe hava durumu kasvetli bir sonbahar'a dönüşmeye başlar, çünkü herhangi bir yerde yapılan en ufak bir değişikliğin neye mal olacağı belirsizdir.

Örneğin MusteriGetir fonksiyonun kullandığı ExecuteSql metodunda text alanlardaki ' karakterlerini sql sunucu yanlış anladığı için '' şekline çevirmeye kalktığınızda MusteriGetir fonksiyonunun zaten '' şeklinde gönderdiğini fark edersiniz ancak diğer fonksiyonlarında bu şekilde çalıştığından emin olamazsınız. Ve günler geçtikçe uygulamada yapılan yama adetleri ve mesai saatleri artmaya başlar.

Bu tür baş ağrılarından kurtulmanın en iyi yolu Unit Test metodlarını kullanmaktır.

Unit Test

Unit test yaparken temel amaç yazdığınız kodun her satırının başka bir kod tarafından otomatik olarak test edilmesidir. Burdaki "test" kullanıcı testi değil, MusteriGetir fonksiyonunun düzgün çalıştığının test'idir.

Her test diğer testlerden bağımsız olarak çalışmalı ve tüm bu testlerin tek bir sonucu olmalıdır yani TRUE yada FALSE. Unit test kodları içerisinde görsel doğrulama (ekrana bir şey yazma, mesaj gösterme v.s.) gibi işlemler yapmazsınız.

UnitTest kodları yanlızca çalışması beklenen durumlar için değil metodun verdiği hataları da test eder. Yani müşteri numarasını - değerde verdiğinizde fonksiyonun hata veriyor olmasını beklersiniz.

UnitTest ile pratikde çıkan sorun genelde işler acil olduğu için bu test kodları sonraya bırakılır. bunu yapmayın! İşiniz ne kadar acil olursa olsun test kodlarını yazmadığınız her metod ileride harcadığınız zamanı tekrar tekrar uzatacaktır.

Code Coverage

Unit test sonuçlarına bakarken kalan açık kalabilecek tek nokta yazdığınız kodun ne kadarının test edildiğini bilmektir. Bunu analiz edebilmek içinde coverage tool'larını kullanabilirsiniz.

Coverage tool'ları unit test kodlarını simüle ederek asıl kod üzerindeki çalıştırılan satırların analizini çıkarırlar. %80 civarındaki bir coverage toplamı genel olarak kabul edilebilir orandadır.

Mock Objects

Unit test yaparken temel amaç yazdığınız tek bir metod'u denemektir. Karmaşık sistemlerde test yaparken tüm uygulamayı çalışır halde tutmak zor olacaktır, bunun yerine sadece gereken ortamı simüle edecek kodlar yazmanız gerekebilir.

Örneğin aslında ldap'a bağlanan Logon() metodunun çağırdığı fonksiyonlar yerine sanki ldap gibi davranan ama fonksiyonel bir iş yapmayan objeler geliştirebilirsiniz. Burda test edilmesi gereken active directory değil yazdığınız logon metodudur.

Bu tür fonksiyon yada class'lara mock objects deniyor. Genelde mock object içermeyen bir unit test fazla fonksiyonel olmayacaktır.

Nasıl ?

Internet üzerinde hafif bir araştırma ile kulladığınız platforma uygun bir unit test kütüphanesi bulabilirsiniz. Kendi kütüphanenizi yazmanız elbetteki mümkündür ancak zaten bu kütüphaneler temelde basit iş yapsalarda testler arasındaki izolasyon gibi konularda gelişmişlerdir. Bu yüzden hazır bir kütüphane kullanmanız faydalı olacaktır.

C# için en popülerlerden NUnit olanı tavsiye edebilirim.

Yeni başlayan projeler için yazdığınız her satırı test eden bir unit yazmayı alışkanlık haline getirebilirsiniz. Kendinize göre bir altyapı oluşturduğunuzda yeni bir test yazmanız çok fazla zamanınızı almayacaktır.

Devam eden projeler için ise en baştan herşeyi test edecek unit'ler yazmanız mümkün değildir ve yürüyen işler yüzünden buna zaten zaman bulamazsınız. Bu yüzden devam eden işlerinizde düzenleme yada yeni eklediğiniz herşey için unit'ler geliştirmeniz mantıklı olacaktır. Böylece düzelttiğiniz kısmın ilerde tekrar sorun yaratmayacağından emin olursunuz.

Mutlaka yazdığınız tüm test'leri tek komutda çalıştaracak bir metod uyarlamanız gerekecektir. Böylece herşeyin yerli yerinde olduğunundan emin olabilirsiniz. Bunun için MSBuild yada NAnt gibi araçları kullanabilirsiniz. Ayrıca kodlama sırasında kodlardan uzaklaşmadan test'leri çalıştıracak testdriven.net gibi visual studio addon'larını kullanabilirsiniz.

Asıl kodlar ile test kodlarını birbirlerinden ayırmak için ayrı kütüphaneler geliştirebilirsiniz ancak bu durumda internal olan bazı fonksiyonları çağıramayabilirsiniz, bu yüzden sadece DEBUG build'lerin test kodları içerecek şekilde düzenlemeler yapmanız iyi olacaktır.

bunun en iyi yolu csharp projeleri için msproj dosyalarını


<Compile Include="UnitTests.cs"  Condition=" '$(Configuration)' == 'Debug' "/>

şeklinde düzenleyerek sadece debug buildlerde bu kodların yer almasını sağlayabilirsiniz.

Örnek

Sonradan gelen bir yorum üzerine hiç örnek vermediğimi farkettim. İcraat görmek arkadaşlar için geliyor ;)

Örneğin aşağıdaki gibi bir class'ımız olduğunu varsayalım.

[codeblock lang="" line="1"]

class Sample

{

public int Sum(int x, int y)

{

if (x == 0)

throw new ArgumentException("x degeri 0 olamaz.", "x");

if (y == 0)

throw new ArgumentException("y degeri 0 olamaz.", "y");

return x + y;

}

public int Multiply(int x, int y)

{

return x * y;

}

}

[/codeblock]

bu class'ı test etmek içinde aşağıdaki gibi yeni bir unit test class'ı tanımlıyoruz.

[codeblock lang="" line="1"]

[NUnit.Framework.TestFixture]

public class SampleTester

{

[NUnit.Framework.Test]

public void SumTest()

{

Sample s = new Sample();

int z = s.Sum(1, 2);

NUnit.Framework.Assert.AreEqual(1 + 2, z, "Hatali toplama islemi");

}

[NUnit.Framework.Test]

public void MultiplyTest()

{

Sample s = new Sample();

int z = s.Multiply(1, 2);

NUnit.Framework.Assert.AreEqual(1 * 2, z, "Hatali toplama islemi");

}

}

[/codeblock]

class'In en tepesine yazılan NUnit.Framework.TestFixture bu class'ın bir unit test class'ı olduğunu belirtiyor (-ki unit test hangi class'ları çalıştıracağını anlayabilsin). Ayrıca class public olarak tanımlanması gerekli aksi halde dışardan ulaşılamaz.

Daha sonra Sample class'ı içerisindeki fonksiyonları test edecek yeni fonksiyonları tanımlıyoruz ve çağırılmasını istediğimiz class'ın üzerine NUnit.Framework.Test attribute'ını ekliyoruz.

Sample üzerindeki fonksiyonlarını çalıştırdıktan sonra elde edilen sonuç ile beklenen sonuçları karşılaştırmak içinde Assert'i kullanabiliriz. Assert üzerinde daha bir sürü farklı metodlar var gerekirse inceleyebilirsiniz.

Assert'in yaptığı iş; eğer sonuç beklenen ile aynı değil ise bu hatayı ver demek.

En çok yapılan ama yapılmaması gereken test aynı anda birden fazla şeyi test etmemek, örneğin;

[codeblock lang="" line="1"]

[NUnit.Framework.TestFixture]

public void YapilmamasiGerekenTest()

{

Sample s = new Sample();

int z = s.Sum(1, 2);

z = s.Multiply(2, z);

NUnit.Framework.Assert.AreEqual(2 * (1 + 2), z, "Hatali toplama islemi");

}

[/codeblock]

Burdaki gibi test maddi olarak doğru ama manevi olarak yanlıştır. Daha sonra herhangi bir sorun çıktığında hatanın nerde olduğunu anlamak için daha fazla zaman harcarsınız. Assert'in hata verdiğini varsayarsak, hatanın toplamamı yoksa çarpma işleminde mi olduğunu anlamanız zor olacaktır.

Ayrıca bazen hata durumlarınıda test etmeniz gerekebilir, örneğin;

[codeblock lang="" line="1"]

[NUnit.Framework.ExpectedException(typeof(ArgumentException))]

public void ExceptionTesti()

{

Sample s = new Sample();

int z = s.Sum(0, 2);

}

[/codeblock]

Bu test'de Assert'i kullanmamıza gerek yok çünkü kod içinde ArgumentException exceptionın gelmesini bekliyoruz, eğer bu exception gelmez ise test başarısız olacaktır.

5 comments

Comment from: ali [Visitor]
ali

A?z?na sa?l?k t?k. ederiz

16/12/07 @ 23:44
Comment from: ömer [Visitor]
ömer

eyvallah baba ,gayet yararl? oldu.

02/05/08 @ 05:00
Comment from: burak [Visitor]
burak

t?k.

09/10/08 @ 05:11
Comment from: Turker Tunali [Visitor]
Turker Tunali

Cok guzel, devamini dilerim.

20/06/09 @ 15:46
Comment from: ramazan [Visitor]
ramazan

bilmedi?im bi konuydu te?ekürler.

11/01/13 @ 07:59

Leave a comment


Your email address will not be revealed on this site.
  
(For my next comment on this site)
(Allow users to contact me through a message form -- Your email will not be revealed!)
Text Renderers:
 

©2017 by Ertan Tike

Contact | Help | Blog template by Asevo | blog tool | dedicated servers | authors