Birim Testler


10 Mart 2013     Etiketler: C# BirimTest UnitTest

Birim test (unit test) nedir? Test edilebilirlik neden önemlidir?


Bu makale F5 Dergi Kasim-Aralik 2012 sayisinda yayinlanmistir.

Birim Testler

Geçtiğimiz sayımızı takip eden okuyucularımızın da hatırlayacağı gibi ASP.NET MVC makalemizde model bağlama konusunun yanı sıra aksiyon metotlarımızın test edilebilirliği ve bunun neden önemli olduğu konularını incelemiştik.

Bazı okuyucularımız birim testler ve test edilebilirlik kavramlarının neden önemli konular olduğunu incelememizi talep ettiler. Biz de bu makaleyle bu talepleri karşılamak istedik.

Başlangıcı 1970’lere kadar uzanan birim test kavramı kimilerinin çok külfetli bir iş olarak gördüğü ancak son yıllarda kimilerinin vazgeçilmezi haline gelmiş olan bu disiplin, kullanmasa da her yazılımcının en azından teorik olarak bilmesi gereken bir konudur.

Yazılım projelerinin ana koda paralel olarak yazılan birim testlerle desteklenmesi kod kalitesi ve sürdürülebilirliği arttırmayı ve amaçlayan bir disiplindir. Birim testler özellikle büyük ekipler tarafından geliştirilen yazılım projelerinde kod kalitesini arttırmalarının yanında yazılımcıların başkaları tarafından yazılan kodları anlamalarını büyük ölçüde kolaylaştıran bir nevi açıklayıcı dokümantasyon görevi görürler.

Birim testlerin beraberinde getirdiği külfetlere rağmen yazılmalarının başlıca sebebi proje ilerledikçe kod üzerinde yapılan değişikliklerin projenin diğer kısımlarında beklenmedik bir yan etki yaratmamasıdır. Ekipteki yazılımcılar tarafından değişiklikler yapıldıkça projeye ait birim testler çalıştırılır ve eğer negatif sonuç veren birim testler varsa bunlar derhal mercek altına alınır ve problem çözülerek birim testler yine çalıştırılır. Bu döngü bütün testler pozitif sonuç verene kadar uygulanır.

Bu disiplinle geliştirilen projeler de birim testler sıklıkla çalıştırılacağından problemler çok daha erken ve tamir edilmesi çok daha kolayken fark edilirler.

Birim test nedir?

MANTIK içeren bir kod parçasının doğru çalışıp çalışmadığını kontrol etmek amacıyla yazılmış kod birim testtir.

Örnek olarak aşağıdaki XML metninden bilgi ayıklama işleminde kullanacağımız bir C# sınıfı düşünelim. Örnek XML: <konfig sunucu="f5dergi.com" protokol="https" port="443"> </konfig>

public class KonfigOku {
  private XDocument xml = null;
 
public KonfigOku(string xml) {
    try {
this.xml = XDocument.Parse(xml); }
   
catch (Exception e) { throw new Exception("Xml metni okunamıyor.", e); }
 
}
  public string Sunucu {

    get {
     
if (xml.Root.Attribute("Sunucu") != null)
        return xml.Root.Attribute("Sunucu").Value;
     
throw new Exception("Sunucu okunamıyor.");
   
}
 
}
}

KonfigOku adıyla yarattığımız sınıfta mantık içeren iki kısım var: yapıcı (constructor) metot ve Sunucu alanı.

Bu demek oluyor ki bu sınıf için en az iki birim test yazmalıyız. Şimdi bu sınıf için yazılabilecek şu birim testleri inceleyelim:

Birim Test 1:
public bool KonfigOkuYapıcıMetotXmlMetniOkunamıyor()
{
 
try {
    //özellikle kötü xml ile dene

    KonfigOku konfigOku = new KonfigOku("");
    return false;
 
}
 
catch (Exception e) {
    //gelecek hatayı kontrol et
return ("Xml metni okunamıyor." == e.Message);
 
}
}

Bu birim test yapıcı metodu test etmekle sorumlu olup test metodun yanlış formatta XML metni gönderilmesi durumunda bilinçli olarak hataya düşecek olmasını ve Exception.Message alanındaki hata mesajını test etmektedir. Test metodumuz beklenen hata gerçekleşirse true beklenen hata gerçekleşmezse false döndürecektir.

Birim Test 2
public bool KonfigOkuSunucuAlanındanBaşarılıOkumaYap() {
 
KonfigOku konfigOku = new KonfigOku("<konfig sunucu="\"f5dergi.com\"" protokol="\"https\"" port="\"443\""></konfig>");
 
return (konfigOku.Sunucu == "f5dergi.com");
}

İkinci birim testimiz doğru XML ile çağırılacak yapıcı metodun akabinde Sunucu alanından okunan değerin doğruluğunu tespit etmektedir.

Birim Test 3
public bool KonfigOkuSunucuAlanındanBaşarısızOkumaYap() {

  KonfigOku konfigOku = new KonfigOku("<konfig protokol="\"https\"" port="\"443\""></konfig>");

  try {

    string s = konfigOku.Sunucu;
    return false;
 
}
  catch (Exception e) {
   
return ("Sunucu okunamıyor." == e.Message);
  }
}

Üçüncü birim testimiz imla olarak doğru ancak Sunucu bilgisi içermeyen bir XML kullanıldığında Sunucu alanından dönecek hatayı doğruluyor. Birinci test gibi beklenen hata gerçekleşirse testimiz başarılı aksi halde başarısız olmuş olacak.

Üç birim test ile kapladığımız KonfigOku sınıfı ilerde kod üzerinde yapılacak değişikliklere karşı korunmalı bir halde artık. KonfigOku sınıfındaki işlev ve mantığa bağımlı olarak geliştirilecek kodlar bu birim testler pozitif sonuç verdiği müddetçe çalışacaktır.

Farz edelim ki bu sınıf üzerindeki Sunucu alanından okuma yapan bir kod parçası yazdık. Bu kod parçasında Sunucu alanından fırlatılacak hatayı (Exception) yakalayıp alternatif bir işlem yaptığımızı düşünelim. Günler hatta haftalar sonra ekibimizdeki başka bir programcının kendi gereksinimlerine paralel olarak Sunucu alanında Exception fırlattığımız satırı return null; olarak değiştirdiğinde KonfigOkuSunucuAlanındanBaşarısızOkumaYap adlı birim test negatif bir sonuç verecek ve buna sebep olan programcıyı uyaracaktır. Birim testlerin olmadığı ortamlarda bu tip kazaların ve doğuracağı problemlerin varlığı çok daha geç ortaya çıkacak ve tespiti çok daha uzun zaman alacaktır.

Somut bir biçimde asıl kodun birim testlerle desteklenmesini ve bunun kod üzerinde nasıl koruyucu bir etkisi olduğunu görmüş olduk. Ancak bu örneğimizde dikkat ederseniz birim testlerimizi normal koddan farklı bir şekilde yazmadık. Birim testlerin hakiki anlamda faydalı olabilmeleri için bir takım özellikleri sağlaması beklenir. Birim testler:

1. Tek bir işlevi net bir şekilde test etmelidir Bir birim test metodu içinde birden fazla işlev test edilmemelidir. Adı üstünde, birim testler test edebilecekleri en küçük birimi test etmelidirler.

2. Otomatik olarak çalışabilmelidir Birim testleri çalıştırmak ve sonucunu almak karmaşık olmayan ve dışarıdan müdahale gerektirmeyen bir işlem olmalıdır. Bir tık ile projeye ait bütün birim testler çalıştırılabilmelidir. BAŞLAT komutu haricinde veri girişi gerektirmeyecek şekilde yazılmalıdırlar.

3. Az bir zaman zarfında çalışıp sonuçlanmalıdır Birim testler mümkün olan en kısa zamanda çalışıp sonuçlanacak şekilde yazılmalıdır ki projeye ait bütün birim testler saatler almamalıdır. Projeye ait birim testler ne kadar uzun sürerse o kadar az çalıştırılacaklarından hataların çabuk fark edilmesinde olumsuz bir etki yaratacaktır.

4. Geçerliğini kaybetmemelidirler Birim testler proje üzerindeki değişiklerden etkilenmeyecek bir şekilde yazılmalıdır. Proje değiştikçe geçerliliğini kaybeden birim testler yanıltıcı negatif sonuçlar doğuracağından projenin gelişimi üzerinde negatif bir etki yaratacaklardır.

5. Dış etkenlerden bağımsız olarak çalışabilmelidirler Birim testler web servis, veri tabanı, gibi dış etkenlere bağımlı halde yazılırlarsa taşınabilirliği azalacaktır. Bir programcının makinesinde çalışan birim test bir başka programcının makinesinde çalışmayacak veya yanlış sonuç verecektir.

Birim testleri yukarıdaki prensiplere uygun yazılmanın yanında bir test çerçevesi (test framework) içinde organize edilmelidir. Birim testleri manuel olarak organize etmek hem zahmetli hem de hataya yol açabilecek bir yaklaşım olacaktır. MSTest, XUnit, NUnit gibi çeşitli test çerçeve sistemleri mevcuttur. Bu sistemler Visual Studio gibi yazılım geliştirme ortamlarına entegre bir şekilde çalışabildiklerinden oldukça faydalıdırlar. Günümüzde artık bir çerçeve sistem kullanmadan birim test yazmak söz konusu bile olmamalıdır.

Daha çok teorik bir giriş formatında planladığımız bu makalemize az da olsa Visual Studio’nun bir parçası olan MSTest çerçeve sistemini tanıtarak devam edelim.

Visual Studio ortamında “Unit Test Project” proje taslağıyla yaratabileceğimiz bu özel proje taslağı MSTest için gerekli referansların bizim için otomatik olarak ekleyecektir. MSTest sınıf ve metotları bir takım özel test nitelikleri ile dekore etme esasına dayalı bir sistemdir. Bu sistem test metotları içerisinden “doğrula” anlamına gelen Assert yardımcı sınıfı ve bu yardımcı sınıfa ait bir takım statik metotlar ile test edilmekte olan birimlerin işlevlerinin doğru çalışıp çalışmadığını kontrol etmek prensibine dayalı çalışır. KonfigOku adlı sınıfımızı MSTest sistemine dayalı bir test projesi içinde test etmek için, projemize Unit Test Project taslağında yeni bir birim test projesi ekleyelim ve aşağıdaki sınıfı yazalım.

KonfigOku Birim Testleri

[TestClass]
public class KonfigOkuTestleri {


  [TestMethod]

 
public void KonfigOkuYapıcıMetotXmlMetniOkunamıyor() {
     try {
       //özellikle kötü xml ile dene
      
KonfigOku konfigOku = new KonfigOku("");
     }

     catch (Exception e) {

       Assert.IsTrue("Xml metni okunamıyor." == e.Message);
    
}
   }

   [TestMethod]
   
public void KonfigOkuSunucuAlanındanBaşarılıOkumaYap() {
      
KonfigOku konfigOku = new KonfigOku("<konfig protokol="\"https\"" port="\"443\""></konfig>");
       Assert.IsTrue(konfigOku.Sunucu == "f5dergi.com");

    }
   
    [TestMethod]
    public void KonfigOkuSunucuAlanındanBaşarısızOkumaYap() {
      
KonfigOku konfigOku = new KonfigOku("<konfig protokol="\"https\"" port="\"443\""></konfig>");
      
try { string s = konfigOku.Sunucu; }
      
catch (Exception e) { Assert.IsTrue("Sunucu okunamıyor." == e.Message); }
    }
}

[TestClass] ve [TestMeteod] adlı niteliklerle dekore ettiğimiz sınıf ve metotlar bu niteliklerle dekore edildiklerinden dolayı Visual Studio tarafından kolaylıkla tespit edilebilirler. Bu da birim testlerin sağlaması gereken özelliklerden ikincisi olan otomatik olarak çalışabilme özelliği konusunda büyük bir kolaylıktır. Hatta Visual Studio çok basit bir şekilde projemizi her derlediğimizde bütün birim testleri çalıştıracak şekilde ayarlanabilir.

Böyle bir konfigürasyon ile yaptığımız değişiklik projenin her hangi bir yerinde bir hataya sebep olursa derleme yapar yapmaz negatif sonuç veren birim testleri fark edip gerekli kontrol ve tamiri yapabilecek duruma gelmiş oluruz.

Birim testleri, birim testlerin neyi amaçladığını ve birim test çerçeve sistemlerinin faydalarını kısaca özetledik. Belirli bir külfet getirdiğini kabul etmekle birlikte özellikle sistemlerin omurgası durumunda olan ve iş mantığı içeren orta katmanlarda uygulanmasını önemle tavsiye ettiğimiz birim testler konusu yazılımcılar tarafından en azından iyi anlaşılması gereken bir konu olduğunu düşünüyoruz. Son yıllarda MVC teknolojilerinin popülerlik kazanması test edilebilirliğinin klasik teknolojilere kıyasla çok daha fazla olmasındandır.

Yorum, istek ve düşüncelerinizi lütfen bizimle paylaşın.

http://f5dergi.com/Iletisim veya bilgi@f5dergi.com




Yorumlar


Bu makaleye ait yorum bulunamadı.

Sizin yorumunuz

Email


Adınız







F5 Dergi © 2017