| « nightshift | Daha iyi program nasıl yazılır ? » |
Unit Test
Yazdığınız bir fonksiyonun herzaman için düzgün çalıştığından nasıl emin olursunuz ?
Follow up:
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.