|
Test Otomasyonu – NUnit/JUnit |
|
Uzun bir aradan sonra merhaba. Bu yazıda test otomasyonu üzerinde durarak, geliştirdiğimiz yazılımın kalitesini artırmaya yönelik neler yapabileceğimizi tartışacağım. Son yıllarda geliştirilen ve çoğunu ücretsiz olarak Internet’ten elde edebileceğimiz gereçlerle ürünümüzün kalitesini artırmak ve kendimiz için bazı işleri kolaylaştırmak artık mümkün. Bugünkü yazılımın temel sorunlarından bir tanesi pazara çıkma süresinin gittikçe kısalıyor olması, ancak iyi bir yazılımdan beklenenlerin de gittikçe artması. Artık ürünümüzün sadece belli bir işi yapıyor olması, yani fonksiyonel becerileri yeterli değil. Kolay kullanılabilir olması, erişilebilir olması, entegrasyona açık olması, yüksek performans göstermesi, vs. gerekiyor. Bütün bunlara ek olarak, yazılım ürünleri gittikçe kurumsal hayatın daha da önemli bir parçası haline geldiklerinden artık bir saatlik bir kesinti bile kullanıcılarımızı mutsuz ediyor. Kullanıcılarımız kullandıkları sistemin, zamanla gelişmesini ve yeni ihtiyaçlara karşılık vermesini beklerken, sorunsuz olarak kullanageldikleri kısımların da yeni sorunlar çıkarmadan çalışmaya devam etmesini bekliyorlar. Başka bir deyişle, bir taraftan artık programların neredeyse sıfır hatayla pazara sunulması gerekiyor; diğer taraftan da programa yeni versiyonlarla eklenen yeni özelliklerin mevcut özellikleri kırıp dökmeden çalışması. Elbette yeterli miktarda test yapabilsek ne pazara hatalı ürünler çıkarırız, ne de çalışan programların yeni versiyonlarda çalışmaz hale gelmesine izin veririz. Ama maliyetler, rakipler, proje takvimleri derken bunlar gözden kaçabiliyor. Elimizde ürünün ne kadarını test ettiğimizi bize söylecek olgun gereçler bulunmaması da işimizi zorlaştırıyor. Terzi Kendi Söküğünü Dikerse – Birim Test Otomasyonu Bir yazılım evi ya da bir bilgi işlem bölümü basit bir tanımla, müşterilerinin işlerini daha kolay, daha hızlı, tekrarlanabilir ve daha az hatayla yapması için otomasyon çözümleri geliştirir. Peki kendisinin sürekli yaptığı – yapması gereken – bazı işler için yazılımı neden kullanmasın? Bu işlerden birisi ve en kolayı test otomasyonu. Farklı seviyelerde test otomasyonu yapmak mümkün. Ben bu yazıda en alt seviyede birim test (unit test) otomasyonu üzerinde duracağım. Birim test otomasyonu, biraz da modern programlama dillerinin (Java ve C#) yaygınlaşmasını bekledi bugün bulunduğu yere gelebilmek için. Yoksa her gün aynı testi yapmaktan usanan bir testçi, beklenen sonuçlarla programın son yapımının (burada ‘build’ teriminin karşılığı olarak kullanıyorum) vereceği sonuçları karşılaştırmak için oturup bir Perl programı yazıyordu. Nesneye yönelik programlama dillerinin yaygınlaşması test geliştirmeyi de kolaylaştırdı herkes için. Nasıl mı? Nesneye yönelik programlamanın getirdiği kurallardan birisi soyutlama. Bu kural, her nesneyi tek başına anlamlı veri ve metodlardan oluşan bir varlık olarak tanımlamanızı gerektiriyor. Böylece, tek başına ele alınabilecek, belli bir halde (state) bulunacak ve belli bir davranışı (behavior) gösterecek bir birim geçiyor elimize. Mesela basit bir banka hesabını ele alalım. Bu Hesap nesnesinin bir kaç özelliği olacak: Hesap Numarası, Hesap Sahibi, ve açık olup olmadığını takip eden Acikmi gibi. Yeni bir hesap açarken hesap numarası ve sahibinin adını belirtmemiz gerekecek ve hesap sıfır bakiye ile açılacak. Hesaba açık olduğu sürece para yatırabiliriz ve hesapta para yoksa da kapatabiliriz. Örneklerimizde C# dilini ve ona özel olarak geliştirilmiş olan NUnit’i kullandım. Örnek çözümü Visual Studio .NET 2003 ile geliştirdim, siz de bilgisayarınıza yükleyip deneyebilirsiniz. NUnit, www.nunit.org’dan indirebileceğiniz bir otomatik birim test gereci ve kullanımı çok kolay. Java dünyasında da aynı amaçla JUnit kullanılıyor. NUnit’leri çalıştırmadan önce Ctrl+B ile projenizi hazırlamayı unutmayın. Hesap sınıfına yakından bakmak için Hesap.cs dosyasını inceleyebilirsiniz. Hesap sınıfı bir hesap numarası ve hesap sahibi ile bir hesap açmamızı, hesap açıkken hesaba para yatırmamızı, para çekmemizi sağlayan ve kapandıktan sonra başka bir işlem yapmamıza izin vermeyen bir sınıf. Test otomasyonunun iki faydası olacak: İlki, Hesap sınıfı gerçekten istediğimiz şeyleri yapıyor mu: dikkat ederseniz kodumuzda bir hata var. Birim test otomasyonu kullanarak üzerinde günlerdir çalıştığımız ve artık bakar kör olduğumuzdan göremediğimiz bu hatayı en dar kapsamda, yani hataya en yakın noktada yakalayacağız. Örnek uygulamamızda birim testlerimizi HesapProgramıTesti projesinde topladık. NUnit testleri uygulamamızdan ayrı olarak Windows control library projeleri olarak geliştiriliyor. NUnit testleri yazarken genel pratik önce sınıfları tek tek test ederek başlamak. Böylelikle en temel bileşenlerimizin her zaman doğru şekilde çalıştığından emin olabiliriz. Uygulamamızdaki her bir sınıfı test etmek için bir test sınıfı kullanmak da kabul gören pratiklerden birisi. Hesap sınıfı testlerini HesapTesti sınıfında topladım. Sınıflar birbirini kullanmaya başladığında da bu pratiği devam ettirmekte fayda var. Örneğin BankaSubesi gibi bir sınıf bir çok Hesap ile birlikte çalışacaktır. Hesap sınıfını en alt seviyede, kendi içerisinde test etmek, daha sonra BankaSubesi’ni kendi test sınıfında hesaplarla beraber test etmek düşünülebilir. Tabii ki siz kendinize uygun farklı pratikler geliştirebilirsiniz. Örneğin ben dört beş sınıfın beraber çalışması gereken uzun testleri bazen ayrı bir test sınıfında toplarım, böylece bu testler daha gözle görünür olur.
Test sınıfımızı bir NUnit test sınıfı olarak tanımlamak için [TestFixture] ile işaretliyoruz (NUnit C# dilinin attribute özelliğini kullanarak sınıfınızı bir test sınıfı olarak belirlemenize izin veriyor, JUnit için ilgili dökümanlara bakmanızı öneririm.) Birim testlerinde gözardı etmemek gereken önemli bir nokta negatif ve pozitif testler. Pozitif testler uygulamamızın ‘işler yolunda giderken’ nasıl çalıştığını test ederken negatif testler de örneğin yüklenmesi gereken bir dosyanın diskte bulunamadığı, ya da ağ iletişiminde sorun olduğunda sınıfımızın nasıl davrandığını test ederler. Hesap sınıfımızın uzmanlık alanı para çekmek ve yatırmak olduğundan, negatif testlerde hesapta yeterince para olmaması ya da kapanmış hesaba para yatırılması gibi durumları test edeceğiz. Pozitif ve negatif testleri net olarak her zaman birbirinden ayıramasak da genellikle bir istisna (exception) oluşturan durumları negatif testler olarak gruplayabiliriz. Test isimlerinde testin negatif ya da pozitif olduğunu belirtmek de yine uygulayabileceğiniz bir pratik. Böylece elinizdeki testlere bakarak hangi tarafta daha fazla test yapmanız gerektiğine dair bir fikir edinebilirsiniz.
Şimdi isterseniz kodumuzdaki hatayı bulmadan önce HesapAcma_Pozitif testine biraz daha yakından bakalım. NUnit kurallarına göre her test (test case) [Test] ile işaretlenmelidir. Biz de bu şekilde HesapAcma_Pozitif’i bir test olarak belirttik. Peki bu test ne yapıyor? Test kodu öncelikle yeni bir hesap nesnesi oluşturuyor. Daha sonra da Assert sınıfının statik yöntemlerini kullanarak hesap nesnesinin doğru bir halde (state) olup olmadığını test ediyor. Bu tip basit durum (trivial case) testlerini her test sınıfının yaptığı ilk testler olarak geliştirmekte fayda var, böylece basit hataları daha karmaşık testleri analiz ederek bulmanıza gerek kalmaz. Ben bu testte aslında bir değil, dört ayrı bilgiyi test ettim ki, bu da NUnit’in olumlu bir yönü. Basit her bir özellik (property) için ayrı testler yazmak zorunda kalmadan Assert sınıfı yardımıyla test geliştirmek için gereken zamanı kısaltabiliyorum. Şimdi gelin başka bir pozitif testle kodumuzdaki hatayı bulalım.
Bu testleri çalıştırmak için NUnit’in grafik arabirimini kullanacağım:
NUnit’i makinanıza kurduğunuzda NUnit’in grafik arabirimini Programs | NUnit V2.1 | NUnit-Gui kısayoluyla başlatabilirsiniz. File menüsünden New Project deyip bu test konfigürasyonunu diskinize kaydettikten sonra Project menüsünden Add Assembly diyerek HesapProgramiTesti.dll’i projemize ekleyelim. Daha sonra ekrandaki Run tuşunu kullanarak testlerimizi çalıştırabiliriz. Birim testleri çalıştırdığımızda testlerden bir tanesi ‘Kapalı bir hesaptan para çekemezsiniz!’ hatası verdi. Halbuki biz bu testin başarıyla çalışmasını bekliyorduk. Bu durumda Hesap.cs dosyasına dönüp bu istisnanın geldiği yere bir bakalım, ve evet! 81. satırda hesap açıksa istisna oluşturuyoruz. Halbuki bu satırın, ParaYatir yöntemindeki 67. satırla aynı olması gerekirdi. Böylece ilk hatamızı bulduk. Programı düzelttikten sonra artık eminiz ki bir daha yanlışlıkla programcılarımızdan birisi bu kodu bozarsa, NUnit bu hatayı yakalayıp bize bildirecek. (Bu hatayı düzelttikten sonra HesapTesti.cs dosyasındaki diğer testleri de /* ve */ satırlarını silip Rebuild all komutunu çalıştırdığınızda NUnit-Gui kendisini güncelleyecektir. Şimdi tekrar Run komutunu kullanarak tüm testleri çalıştırabilirsiniz.) Bundan Sonra Yazımızın amacı bir NUnit kullanım kılavuzu yazmak değil. Bunun için yeterli yerimiz de yok. Ama örnek çözümümüzü kullanarak ve aşağıdaki bağlardaki dökümanlara da göz atarak biraz daha haşır neşir olabilirsiniz. Ben biraz daha genel test otomasyonu propagandası yapıp yazımı bitirmek istiyorum. Test otomasyonu, Extreme Programming ile – tekrar – gündeme geldi. Ancak illa da XP kullanılan bir süreçte kullanılması gerekmiyor. Bir çok yerde Teste Dayalı Geliştirme gibi isimler altında başlıbaşına yeni bir geliştirme modeline yol açtı. Bu açıdan tek başına Şelale’yi (Waterfall – yazılımı analiz, tasarım, geliştirme, test ve piyasaya çıkış olarak zaman içerisinde peşpeşe gelen süreçlerle geliştirme modeli) hafifçe değiştirerek yazılım geliştirme sürecinize katmanız mümkün. Eğer test süreciniz halihazırda birim testi (kodu geliştirenin yaptığı modüler test) ve sistem testi (ayrı bir test/kullanıcı ekibinin yaptığı daha üst seviyede test) gibi iki aşamada yapıyorsanız, tek yapmanız gereken birim test süreci ile geliştirme sürecini aynı zaman diliminde gerçekleştirmek. Bu modelde geliştirici yazdığı yazılımı her bir sınıf ya da özellik için gerekli testleri yazmadan bir sonraki sınıf ya da özelliğe geçmez. Projede ilerledikçe eğer önceden geliştirilen sınıflarda değişiklikler yapılırsa test sınıfları da gerekli şekilde düzenlenir. Tek bir testte hata varsa, yazılım geliştirici o hatayı gidermeden yeni bir işe başlamaz. Her gün ya da – daha mantıklısı – her gece tüm sistemin otomatik olarak yeniden yapılması (nightly build) ve otomatik testleri çalıştırması bu süreci tamamlar. Böylece yazılım geliştirici sabah işe geldiğinde son yapıma karşı çalışmış testleri analiz eder ve hatalar çıkmışsa, ilk iş olarak bunları düzeltir. Test tabanlı geliştirmenin ilk anda göze çarpmayan bir faydası da kodunuzun yanısıra tasarımınızı test etmesi. Eğer sınıflarınızı kolaylıkla test edemiyorsanız tasarımınızı gözden geçirmenizde fayda vardır. Test edilebilir sınıflar tek başlarında ayakta durabilen sınıflardır ve ileride tekrar kullanılmaları ihtimalleri daha kuvvetlidir. Ne Kadar Test? Peki ne kadar test yazmalı? Şu var ki otomatik testler, yapıp yapacağımız testlerin bir alt kümesi. Yine de hedefimiz mümkün olan her kod satırını ve kodun geçebileceği her yolu (if ... else ... blokları) test etmek olmalı. Kodun ne kadarını test ettiğimiz ölçmek için genellikle kod kapsama gereçleri (code coverage tools) kullanırız. Şu anda Java dünyasında kod kapsama ürünleri bulunuyor, .NET için de yakın zamanda ticari ürünler bekleyebiliriz. Bu ürünler bize proje bazında yazdığımız kodun, diyelimki %70’ini test etme hedefi koymayı ve her gün bulunduğumuz yeri görebilme imkanı tanırlar. Elinizde böyle bir gereç yoksa aşağıdaki bağlarda bahsedilen makaleye bir bakabilirsiniz. Bunun dışında yazılan her kod satırı başına bir satır test kodu, ya da her özellik (para çekme, para yatırma gibi) başına bir pozitif iki negatif test gibi kriterler koyabiliriz. Örneğimize bakarsak bu rakamların makul olduğunu görebiliriz. Siz de uygulamanıza has farklı kriterler belirleyebilirsiniz. Mutlu Son? Daha önce de söylediğim gibi otomatik birim testleri, tüm sorunları çözmeye yetmiyor. Kullanıcının sistemle bire bir muhatap olduğu Windows ya da Web tabanlı uygulamalarda kullanıcı hareketlerini (fare ile tıklama, F5 tuşuna basılması, sürükle ve bırak gibi) bugün NUnit ile test etmek mümkün değil. Ancak bu alanda da ürünler yok değil. Örneğin WinRunner gibi ürünlerle Windows’ta bir kullanıcının yapacağı bir çok hareketi simule edebilirsiniz. Diğer yandan NUnit’i daha da geliştirerek bu alandaki açığı kapatmak üzere NUnitForms ve NUnitAsp gibi yeni projeler de yine açık kaynak dünyasında geliştirilmeye devam ediyor. Test otomasyonu alanında son yıllarda ortaya çıkan momentuma bakılırsa, yakın zamanda çok daha olgunlaşmış ürünler çıkacağından ve ürünlerimizin kalitesini artırmak istediğimizden, daha gelişmiş gereçlere ulaşacağız gibi görünüyor. O zamana kadar da eldeki birim test otomasyonu gereçlerinden mümkün olduğunca faydalanmanızı öneririm. Bağlar - NUnit www.nunit.org'dan indirilebilir. - Buradan Teste Dayalı Geliştirme ile ilgili diğer sitelere de bağlar bulabilirsiniz: http://www.nunit.org/resources.html - Bu konuda yeni çıkan bir kitap da Test-Driven Development in Microsoft® .NET: http://www.microsoft.com/MSPress/books/6778.asp - Java dünyası ise JUnit’e www.junit.org'dan ulaşıyor. - Uygulamanızı sınıf seviyesinden daha üst seviyede test etmek isterseniz WinRunner gibi ürünlere bir göz atabilirsiniz: http://www.mercuryinteractive.com/products/winrunner/ - MSDN’de yeni yayınlanan mütevazi bir kod kapsama gerecine de göz atmanızı öneririm. Nisan sayısını teste ayıran MSDN Magazine’e buradan ulaşabilirsiniz: http://msdn.microsoft.com/msdnmag/issues/04/04/default.aspx - Java dünyasındaki iddialı kod kapsama gereçlerinden birisi de Sitraka JProbe: http://www.quest.com/jprobe/ Yazıyla ilgili görüş ve yorumlarınızı
keremkiziltunc@hotmail.com
veya yorum@teknoTurk.org adreslerine
yollayabilirsiniz. |