25 Mayıs 2014 Pazar

Unit Testing Örnek Uygulama


Merhabalar arkadaşlar;
  Geçen hafta sizlere unit test yaparken kullanılan assert metodlarından bahsetmiştim. Bu hafta ise temel düzeyde daha önceden yapmış olduğumuz unit test örneğini NUnit kullanmadan Visual Studio'nun kendi test aracı sayesinde yapcağız. Şimdi Visual Studio üzerinde bir test projesi nasıl oluşturulur ve bir birim test nasıl yazılır inceleyelim. Öncelikle, test etmek istediğimiz sınıfın aşağıdaki gibi olduğunu düşünelim.

public class HesapMakinesi
    {
        public int Topla(int sayi1, int sayi2)
        {
            return sayi1 + sayi2;
        }
    }

Örnek olması açısından basit ve aslında biraz anlamsız bir sınıf oluşturdum. Yaptığı tek işlem iki sayıyı toplamak ve sonucu döndürmek. Bu sınıfa test yazmak için öncelikle bir test projesi oluşturmamız gerekiyor.

Bunun için “Solution”’ sağ tıklıyoruz ve açılan menüden “Add”i seçip daha sonra “New Project”i seçiyoruz. Aşağıdaki gibi bir ekran açılmalı:
























  Daha sonra sol taraftaki menüden “Test”i seçiyoruz ve ortadaki menüden “Unit Test Project”i seçiyoruz ve “OK”e basıyoruz. Burada unutulmaması gereken bir nokta da test projenize anlaşılır bir isim vermek; bu isimlendirme sırasında ben genellikle test edilecek sınıfın olduğu projenin adına “.Test” ekleyerek bir isim oluşturmayı tercih ediyorum. Benim test edeceğim sınıfın bulunduğu projenin adı "TestSinifi" dolayısıyla test projemin adı da  "TestSinifi.Test".

  Bu işlemi yaptıktan sonra Visual Studio bizim için yeni bir proje ve bir sınıf oluşturdu. Bu sınıfın adı “UnitTest1”; oldukça anlamsız, dolayısıyla “Solution Explorer”da yeni oluşan sınıfa sağ tıklayalım ve “Rename” diyerek ismini değiştirelim. Bu isimlendirme sırasında yine belirli bir standart takip etmeniz yararınıza olacaktır. Örneğin test edeceğim sınıfın adı “HesapMakinesi” olduğu için test sınıfımın adını “HesapMakinesiTest” olarak belirledim.

İlk testimizi aşağıdaki gibi ekleyelim:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace TestSinifi.Test
{
    [TestClass]
    public class HesapMakinesiTest
    {
        [TestMethod]
        public void HesapMakinesi_Topla_Test()
        {
            int beklenenDeger = 34;

            int sayi1 = 32;
            int sayi2 = 2;

            HesapMakinesi hesapMakinesi = new HesapMakinesi();
            int gercekDeger = hesapMakinesi.Topla(sayi1, sayi2);

            Assert.AreEqual(beklenenDeger, gercekDeger);
        }
    }
}
  Burada yaptığımız işleme dikkat edersek, öncelikle bu testin sonunda dönmesini beklediğimiz değeri tanımladık:

int beklenenDeger = 34;

Daha sonra girdilerimizi ayarladık:

 int sayi1 = 32;
 int sayi2 = 2;

Test edeceğimiz sınıfı oluşturduk ve test edeceğimiz metodu çağırdık. Sonucu da bir değişkene kaydettik:

HesapMakinesi hesapMakinesinew HesapMakinesi();    
int gercekDeger = hesapMakinesi.Topla(sayi1, sayi2);

Son olarak da beklediğimiz değer ve gerçek değerin eşit olup olmadığını kontrol ettik:

Assert.AreEqual(beklenenDeger, gercekDeger);

  Testimizi çalıştırmak için, Visual Studio’da yukarıdaki menüden “TEST->Windows->Test Explorer” seçimini yaptık. Aşağıdaki gibi bir pencere açılmalı.







































  Buradan “HesapMakinesi_Topla_Test”i seçelim ve sağ tıklayıp “Run Selected Tests”i seçelim. Eğer testiniz bu listede görünmüyor ise Test projenizi Build edin.

























  Gördüğünüz gibi testimizin başarıyla geçtiğini belirten yeşil bir tik işareti gördük. Sizde kendiniz yazmış olduğunuz uygulamaları bu yönetemle test edebilrsiniz. Bu sayede hatalarınızı görerek yazılım konusunda kendinizi geliştirebilrsiniz. Bu haftalık anlatacaklarım bu kadar arkadaşlar, haftaya bu zamana kadar yapmış olduklarımızı hatırlayıp genel bir tekrar yapacağız. Görüşmek üzere hoşçakalın..

11 Mayıs 2014 Pazar

Unit Testing Yaparken Kullanılan Assert Metodları

Merhabalar arkadaşlar;
  Geçen haftayı hatırlayacak olursak, kaliteli birim testlerin hangi özellikte olması gerektiğinden bahsetmiştik. Böylelikle kodumuzun düzgün çalışıp çalışmadığını kontrol ederken daha rahat yöntemler kullanırsak, işlerimizin kolaylaşacağını gördük. Bu hafta sizlere unit testing yaparken sıkça kullanılan assert sınıfının içersinden bulunan çeşitli metodları ve ne işe yaradıklarından bahsedeceğim. Assert metodu ile iddia edilen durum doğru ise testimiz başarılı, aksi takdirde başarısız olmuş olur.

Sıklıkla kullanılan assert metodları aşağıdaki gibidir;


  • AreEqual Method
  • AreNotEqual Method
  • AreNotSame Method
  • AreSame Method
  • Equals Method
  • Fail Method
  • Inconclusive Method
  • IsFalse Method
  • IsInstanceOfType Method
  • IsNotInstanceOfType Method
  • IsNotNull Method
  • IsNull Method
  • ReplaceNullChars Method
Assert.AreEqual Method(Object,Object):

Belirtilen iki nesnenin eşit olduğunu doğrular. Nesneler eşit değilse test işlemi başarısız olur.





  Testin başarısız olması durumunda AssertFailedException hatası oluşur. Bu metod için dikkat edilmesi gereken bir diğer nokta ise mantıksal değerlerin eşit olması halinde farklı sayısal türler eşit olarak kabul edilir. Örneğin 26 M 26'ya eşittir.



Assert.AreNotEqual Method(Object,Object):

  Belirtilen iki nesnenin eşit olmaması durumunu doğrular. Nesnelerin eşit olması durumunda onaylama işlemi başarısız olur.








  AreEqual metoduna benzer şekilde, testin başarısız olması durumunda AssertFailedException hatasıyla karşılaşırız.


Assert.AreNotSame Method(Object,Object):

  Birbirinden farklı iki nesne geldiği durumunu doğrular. Eğer iki tane aynı nesne gelmesi durumunda onaylama işlemi başarısız olur.









Assert.AreSame Method(Object,Object,String):

  Belirtilen iki nesnenin aynı olması durumunda çalışır eğer beklenen nesne farklı gelirse test başarısız olur ve ekranda birde hata mesajı görüntülenecektir.




Assert.Equals Method:

İki nesnenin eşit olup olmadığını kontrol etmeye yarar, AreEqual metoduyla aynı işlevi yapar. Bu metodu kullanmaktansa ben sizlere AreEqual metodunu kullanmanızı öneririm.


Assert.Fail Method (String,Object[]):

Herhangi bir koşul denetlemeden onaylama işlemi başarısız olur. Test sonucunda hatanın içeriğini gösteren bir ileti görüntüler ve koşullara uygun biçimde parametre oluşturur.





Testin başarısız olması durumunda AssertFailedException sınıfı çalışır.

Assert.Inconclusive Method (String,Object[]):

  Test'in onaylanma işleminden sonra sonucun oluşmadığı durumlarda ortaya çıkar. Neticesiz bir işlem yaptığınızı anlarsınız. Testin sonucunda bir ileti görüntüler ve sonucu düzeltecek şekilde biçimlendirme uygular.


Bir testin uygulamaya konmadığı zamanlarda AssertInconclusive sınıfı oluşur.

Assert.IsFalse Method(Boolean,String):

  Öncelikle teste ait bir koşul belirtilir fakat belirtilen koşulun doğruluğundan emin değilizdir. Bu durumda IsFalse metodu sayesinde kontrol yapılır ve hatanın içeriğini görüntüleyen mesaj ekrana bastırılır.

Assert.IsInstanceOfType Method(Object,Type,String,Object[]):

  Üretilen nesnenin beklenen sınıfa ait olup olmadığının kontrolü yapılırken uygulanır. Beklenen öğe parametre olarak belirtilerek gelip gelmediği kontrol edilir ve öğe bulunmazsa hata mesajı ekrana bastırılır. Sonuç olarak kullanılacak parametlerde metod içerisinde belirlenebilir.









Assert.IsNotInstanceOfType Method(Object,Type):

  Belirtilen nesnenin beklenen türde bir öğe olmadığı durumda ortaya çıkar. Beklenenin yanlış türde bir nesne olduğu bildirilir. IsInstanceOfType metoduna benzer özellikte çalışır.









Eğer null bir değer gelirse test sonucundan o zaman AssertFailedException hatası meydana gelir.

Assert.IsNotNull Method(Object,String,Object[]):

  Önceki alıştırmada belirttiğim exception olayının kontrolü esasen bu metod sayesinde gerçekleştirilmektedir. Yani bir nesnenin üretilip üretilmediği buradan kontrol edilir.








Assert.IsNull Method(Object):

  Assert.IsNotNull metodunun aksine test tarafından bir nesne üretilmediği zaman çalışan assertion olayıdır. Eğer doğru ya da yanlış bir nesnenin varlığı söz konusu olduğunda fail olacaktır.








Assert.ReplaceNullChars:

  Bir dizede null karakter kullanıldığı zaman ('\0') değeri "\\0" değeriyle değiştirilir. Null karakter kullanıldığı zaman proğramın içeriğini değiştirmek yerine otomatik olarak içersi doldurularak testlerin doğru sonuçlar vermesi sağlanır.






  Görüldüğü üzere arkadaşlar assert metodunun pek çok türü bulunmaktadır. Unit testing yaparken belirtilen koşulların doğru ya da yanlış olması duruma göre testin durumu kontrol edilir. Kullanılan metodlar sayesinde hatanın nerelerde oluştuğu rahatlıkla görülebilir. Bu haftalık anlatacaklarım bu kadar arkadaşlar, assert metodu unit testing konusunun olmazsa olmazlarındandır ve iyi kavranılması gerekmektedir. Haftaya tekrar görüşmek üzere hoşçakalın..


4 Mayıs 2014 Pazar

Kaliteli Birim Testler Hangi Özellikte Olmalıdır?


Merhabalar arkadaşlar;
  Hatırlayacak olursak geçen hafta mock object'lerin ne işe yaradığından ve kullanımından bahsetmiştik. Bu hafta birim test yazarken dikkat etmemiz gereken önemli noktalar üzerinde duracağız. Kaliteli birim testlerin hangi özellikte olması gerektiğinden ve birim test yazarken dikkat etmemiz gereken noktalardan sizlere bahsedeceğim. Birim testler, yazılımın belirli bir biriminin beklenildiği gibi çalıştığını onaylayan ve aynı zamanda ileri bir zamanda yazılımın bu parçasına yapılacak değişikliklerin kodun çalışmasına olan etkisini ortaya koyan kod parçalarıdır.


Tanımdan da anlaşılabileceği üzere, birim testlerin iki önemli işlevi vardır;

1. Belirli bir kod parçasının, doğru çalıştığını gösterme.
2. İleride yapılacak değişikliklerin etkilerini kolayca görebilme.

  Birim testler her ne kadar yazılımın fonksiyonel kodlarından bağımsız olarak gözükse de, gerçekte bir kod parçasına ait birim testlerin kolaylıkla yazılabilmesi için kodun belirli kuralları takip etmesi gerekir. Yani bir kod parçasının birim testlerinin doğru ve kolayca yazılabilmesi için; yazılımın fonksiyonel parçalarının da belirli özellikler göstermesi gerekmektedir. Bu özellikleri aşağıdaki gibi sıralayabiliriz:

  • Arayüz ve davranışın birbirinden ayrılmış olması gerekiyor. Burada daha önceden tanımlanmış tasarım desenlerini kullanabilirsiniz; Model-View-Controller, Model-View-Presenter, Model-View-ViewModel gibi.
  • Sınıflar yerine arayüzler üzerinden programlama yapılmalıdır. Bu sayede dış bağımlılıkları izole etmek kolaylaşmaktadır.
  • Dependency Injection teknikleri kullanılmalıdır. Bu sayede tıpkı arayüzleri kullanmak gibi test etmek istediğiniz kod parçasını daha rahat bir şekilde izole edebilirsiniz.
  • Ve en son olarak, belki de bu yukarıdaki kuralların temeli olarak, kodunuzu mümkün olduğu kadar soyut geliştirin, kod parçalarınız sadece belirli görevler yapsın ve sınırları belirli olan parçalar olarak geliştirmeye dikkat edin.
Bunun dışında iyi tasarlanmış birim testler'de belirli özelliklere sahip olmalıdır. Bunları da kısaca aşağıda ki gibi listeleyebiliriz:

  1. Her birim test genellikle sadece bir doğrulama kuralı içermelidir. Eğer bir birim test içerisinde birden fazla doğrulama kuralı kullanıyor iseniz, muhtemelen birden fazla durumu test ediyorsunuz demektir. Eğer birden fazla doğrulama kuralı işletirseniz, herhangi bir test hata aldığında bu hatayı tespit etmeniz zor olacaktır. Bunun yerine her durum için ayrı birer test yazın ve sadece bir doğrulama kuralı işletin.
  2. Bir birim test tek başına ve herhangi bir ön koşul olmadan çalışabilmelidir. Birim testlerinizin sıra ile çalışması gerekiyorsa bir hata yapıyorsunuz demektir; her birim test bağımsız olarak çalışabilmelidir.
  3. Bütün dış gereksinimler ortadan kaldırılmalıdır. Örneğin, bir kod parçası veritabanına bağlanıp buradaki veriyi değiştiriyor ise bunu ortadan kaldırmanız gerekiyor. Sadece bir fonksiyonun doğru çalışıp çalışmadığını test etmeye çalışıyorsunuz, veritabanının doğru çalışıp çalışmadığını değil.
  4. Gereksiz ön koşulları kaldırın. Testler olabildiğince yalın olmalıdır. Tabi bunun da bir sınırı vardır ve bazen testler çalışmadan önce belirli ön koşulları oluşturmanız gerekebilir.
  5. Birim testlerinize yalın ama anlaşılabilir isimler verin ve belirli bir standardı takip edin. Bu sayede testlerinizin sadece isimleri üzerinden işlevleri hakkında fikir edinmek mümkün olabilir.
  6. Konfigürasyonları test etmeye çalışmayın; çünkü konfigürasyonlar direkt olarak yazılımın kurulduğu veya çalıştığı ortama bağlıdır, dolayısıyla bunları test etmenin herhangi bir getirisi olmayacaktır.

Örnek olarak aşağıdaki XML metninden bilgi ayıklama işleminde kullanacağımız bir C# sınıfı düşünelim. 

<konfig sunucu="orhanoztekin.blogspot.com.tr" protokol="https" port="443"> </konfig>


















  KonfigOku adıyla yarattığımız sınıfta mantık içeren iki kısım var: yapıcı 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:











  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:

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

Birim Test 3:









  Üçü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ı 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.  

KonfigOku birim testleri aşağıdaki gibidir:

[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 == "orhanoztekin.blogspot.com.tr");
    }
   
    [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 [TestMethod] 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 herhangi bir yerinde bir hataya sebep olursa derleme yapar yapmaz negatif sonuç veren birim testleri fark edip gerekli kontrol ve tamiri yapabilecek duruma gelmiş oluruz.

   Yazılım testinin detaylarına inildikçe birim test kısmında görevli arkadaşların omzuna ne kadar yük bindiği daha iyi anlaşılabilir. Yazılımın geliştirme kısmında bulunan arkadaşlara önerim, yazılımlarımızın birim testlerini ve dokümantasyonunu eksiksiz yapmaya çalışalım, çünkü bundan kaynaklanacak hataları test mühendisi arkadaşlar yakalayamayabilir ve hatta yakalasa bile düzeltmek bize çok daha maliyetli olabilir. Bu haftalık anlatacaklarım bu kadar arkadaşlar haftaya unit testing yaparken sıklıkla kullanılan assert metodlarını işleyeceğiz. Görüşmek üzere hoşçakalın..