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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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..
Hiç yorum yok:
Yorum Gönder