Her fırsatta ana kategorilerdeki makaleleri belirli bir seviyeye getirerek biran önce projelerimize başlamak istediğimi söyleyip duruyorum ancak bunun öncesinde bloğa eklemem gereken son bir mini kategori kaldı; Jasmine.

Bu makaleyi 10 yıl önce yazıyor olsaydım, test kodu yazmanın ne kadar faydalı ve gerekli bir iş olduğunu anlatmaya çalışırdım. Ancak şu anda yıl 2013 (En azından ben bu makaleyi yazarken öyleydi) ve bu zamanda test kodu yazmamak, kullanıcı adı ve şifre bilgisini direk kod içerisine gömmek kadar kabul edilemez ve ayıp bir durum (Biraz dramatize ettiğimin farkındayım ama hissettiklerimi yazıyorum).

Gel gelelim, bloğun formatı gereği, her kategorinin başında, o kategorideki teknolojiye neden ihtiyacımızın olduğunu özetledim ve burada da formatı bozmak istemiyorum. Bakalım test kodu yazmaya ve Jasmine kütüphanesine neden ihtiyacımız varmış?


Konuya "Legacy code" terimi ile giriş yapmak istiyorum. Öncelikle bu terimin "bencesini" bir anlatayım. Sonrasında bir üstadın konu hakkındaki görüşlerini paylaşırım.

Temel anlamda diyebiliriz ki, vaktiyle kendini bilmez bir veya bir grup programcının, çeşitli mucitlikler ve Amerika'yı baştan keşfetme sevdaları ile akraba evlilikleri sonucu ortaya çıkarttıkları, sözüm ona, onlar verdiğinde hiç sorunsuz çalışan ve kendileri hiç ellenmemiş tertemiz projelere yelken açarken, "Al bununla da sen ilgilen" şeklinde bize "çaktıkları" programsılardır. Bunlar çoğunlukla bilinen mimari tasarımlara veya Best Practices'e uymaz ve uydurulamazlar. Ayrıca bunların ortak özellikleri, genel olarak bize geçtiği günün hemen ertesinde patlar ve konu hakkında herhangi bir dokümantasyon da bulunmaz. Artık ara ki sorunu bulasın. Aslında aramaya da gerek yoktur. Çoğu zaman programın kendisi sorundur ve eğer ki proje çok büyük değilse, yeniden yazmanın maliyeti, destek maliyetinden daha azdır.

Mimar olana kadarki kariyerim, sürekli birilerinin bıraktığı legacy kodları adam etmekle geçti. Belki bu yüzden olaya biraz duygusal yaklaşıyor olabilirim ama sıfır bir proje dururken legacy kodu devraldığı için mutlu olacak bir programcı tanımıyorum.

Bir de konuyu başka bir üstadın dilinden dinleyelim. Michael Feathers, "Working Effectively with Legacy Code" isimli kitabında der ki, test kodu yazılmamış kod, legacy koddur. Ne kadar süre önce veya kimin tarafından yazıldığının bir önemi yoktur. İstersen beş dakika önce sen yazmış ol. Eğer test kodunu yazmadıysan, bu beş dakika içerisinde senin veya ekipteki başka birinin yazdığı bir kod sebebiyle, ilk yazdığın kodun yazılış amacını hala tam anlamıyla yerine getirip getiremediğinden emin olamazsın.

Evet, belki kodu bir başkası bozmuştur ama bu takımın meselesidir ve belki de öyle bir yeri bozulmuştur ki, Production ortamında çalışıp müşteriye finansal zarar verene kadar kimsenin haberi de olmaz. O halde yazdığımız her kodun, her koşulda, kendinden beklendiği gibi çalıştığını garanti eden test kodunu da yazmalıyız ve bunu takımdaki herkesin yaptığı her değişiklikten sonra otomatik çalışır hale getirmeliyiz ki, böylece benim yaptığım değişiklik sonrasında başkalarının yazdığı belki de binlerce test kodu otomatik olarak çalışsın ve sistem beni "Aha bu adam yaptığı şu değişiklik sebebiyle kodun burasını bozdu" diye tüm takıma ifşa etsin (Ki sonradan müşteriye ifşa edilmeyelim).

Güzel, buraya kadar söylediklerimden bir şeyler çıkartabiliriz. Bir bakalım...
  • Test kodu yazarak, yazdığımız kodun beklendiği gibi çalıştığını garanti ederiz.
  • Testi yazılmış kodu, gelecekte bizim veya başkasının yapacağı değişiklikler sebebiyle iş görmez hale gelme tehlikesinden koruruz.
  • Test kodunu yazarak, yazdığımız kodun başkaları tarafından "Legacy Code" ünvanı ile anılmasının ve ilk fırsatta yeniden yazılmasının önüne geçeriz (Çünkü doğru çalıştığı garanti olan kodu kimse yeniden yazmak istemez).

Bunları cebe koyalım ve devam edelim.

Biz programları neden yazarız? Çünkü müşteri bir şeyler ister ve bunun karşılığında bize para verir. Biz de bizden istenileni tam olarak anlamaya ve yapmaya çalışırız ki, tecrübe ile konuşuyorum, klasik tekniklerle müşteriyi anlayarak istediğini verme konusunda %60'lık bir başarı taktire şayandır.

Çoğunlukla müşteri istediğini tam olarak ifade edemez. Biz de analiz toplantılarına kafamızda bir varsayım ile gideriz ve müşterinin anlattıklarını bu varsayımlara benzetmeye çalışırız. Sonrasında gider, yazılım geliştirme ekibine kafamızdakileri anlatırız. Geliştirme ekibinin elinde de genellikle hazır yazılmış bir miktar kod vardır ve istenen ürünü, bu kodun bir tarafını kırpıp yama yapmak suretiyle kısa sürede ortaya çıkartmaya çalışır. Çünkü müşteri sağlam pazarlık yapmıştır ve proje yöneticisi proje süresini kısa tutmak zorunda kalmıştır.

Peki sonuç ne olur? Ortaya bir ürün çıkar ama bu ürünün müşterinin istediği ürünle pek alakası yoktur. Sonrasında gelsin ek geliştirme talepleri. Bu talepler ücrete tabii olur mu, olmaz mı, ayrı mesele. Ama ilk yazılan (daha doğrusu yamanan) kodun test kodu olmadığı için, yapılan her bir ek geliştirme, sistemin hiç tahmin edilmeyen başka bir noktasında Bug oluşmasına sebep olur. Onu çözeyim derken, alakasız başka bir yer bozulur ve bunun ardı arkası kesilmez.

Bir de işin teknik boyutuna bakalım. Production'a bir program konulur. Ancak programı oluşturan kodların %70'i, aslında yapılacak işle hiç ilgili değildir ve muhtemelen o fonksiyonlara da hiçbir zaman da girilmeyecektir. Ama programın desteğini vermekten sorumlu "Legacy kod kendilerine çakılmış olan" ekip, yapacakları bir değişiklikte, %30 yerine %100'lük bir kodu gözden geçirmek ve bakımını yapmak, belki de yeni teknolojilere geçirmek zorunda kalırlar. Ayrıca dokümantasyon da (eğer ki varsa), yine kodun tamamını içermek durumundadır.

Sonuç olarak başlangıçta fazla zaman gider endişesiyle yazılmayan test kodları sebebiyle, sonradan onlarca kat fazla zaman harcanır da kimsenin ruhu duymaz. Üstelik müşteri memnuniyetsizliği ve yazılımcıların iş tatminsizliği de bonus olarak karşımıza çıkar.

Neyse ki, bu problemler sadece Türklere ait olmamış olacak ki, ecnebiler bu sorunlara çareler bulmuşlar ve gayet başarılı şekilde uygulamışlar. Gariptir ki, (An itibariyle piyasa hakim biri olarak konuşuyorum) biz önümüzdeki başarılı örnekleri taklit etme konusunda bile yeterince başarı gösteremiyoruz. Bu belki de, "Biz zaten yeterince iyiyiz" düşüncesinden kaynaklanıyordur, bilemiyorum.

Neyse, fazla uzatmadan konuyu toparlayacağım. Bu makaleyi bundan birkaç yıl önce yazsaydım, muhtemelen TDD'den (Test Driven Development) bahsederdim. Ancak son zamanlarda TDD'nin yeniden gözden geçirilmesi ve Agile sistemlerindeki User Story'lerle ilişkilendirilmesiyle birlikte artık BDD (Behavior Driven Development) gibi bence çok daha başarılı bir yöntemimiz var.

Bilmeyenler için BDD'nin sürece nasıl dahil olduğunu hızlıca maddeler halinde anlatacağım.
  1. Analist müşteri ile toplantıya gider ve ihtiyaçlarını senaryolar şeklinde alır (User Scenarios). Bunlar genelde, "Yeni bir müşteri geldiğinde onu bilgisayara kaydetmem gerekir, bir tıklamayla geçmiş borçlarını yazıcıya göndermem gerekir" tarzında paragraflardan oluşur.
  2. Analist, müşteriden aldığı senaryoları kullanım durumlarına çevirir (User Stories). Bunlar genelde, "Kullanıcı ekranda şu butona basar, ekranda şunu görür, çıktı olarak şunu alır, şu uç birime bildirim gönderilir" tarzında, daha programsal ifadelerdir.
  3. İdeal olarak test grubu veya imkan dahilinde değilse yazılım ekibi, her bir kullanım durumunu anlatan test kodu veya kodları yazar. Öyle ki, herhangi bir kullanım durumu ile ilgili test kodları çalıştırıldığında, eğer ki hepsi başarılı oluyorsa, o kullanım durumunda söylenen her şeyin eksiksiz yapıldığını garanti ediyor olması gerekir.
  4. Başlangıçta sadece test kodu olduğu ve hiç program kodu olmadığı için, tüm testler başarısız olacaktır. Yazılım ekibi, en dış kabuktan başlayarak, içeriye doğru, her bir test kodunu başarılı hale getirmeye yetecek kadar kod yazar. Burada en önemli konu, sadece test kodlarını başarılı hale getirecek kadar kod yazılmasıdır. Böylece YAGNI prensibinden ödün verilmemiş olunur.

Sonuç? Eğer analist işini doğru yapabildiyse, sadece müşterinin ihtiyaçlarını tam olarak karşılayan, fazladan da ekstra herhangi bir kod bloğunu içermeyen, tüm kullanım durumlarının hatasız çalıştığı test edilmiş bir program ortaya çıkar. Üstelik yazılan her bir satır kod, bir testin ihtiyacını karşılamak için yazıldığından, test edilmeyen tek bir satır bile olmayacaktır ve kimse buna "Legacy Code" demeyecektir. İleride birileri, herhangi bir yerini değiştirdiği takdirde, hiç tahmin etmediği başka bir tarafını bozmuş olsa dahi, hemen o kısım ile ilgili testler hata verecek ve programcıyı uyaracaktır.

Bir de ek geliştirme safhasına bakalım. Diyelim ki müşteri ek bir fonksiyonalite istedi veya mevcut bir fonksiyonun çalışma şeklini değiştirtti. Tek yapılması gereken, öncelikle ilgili kullanım durumunun, ardından o kullanım durumuna bağlı olan test kodlarının yeni isteklere uygun olarak değiştirilmesidir. Bundan sonrasında sadece, yazılım ekibinin kodları, testlerin yeni haline uyarlamasını talep etmek kalır.

Her ne kadar TDD yaklaşımına derin saygı duysam da, BDD ile tanıştıktan sonra bu yaklaşımı çok daha başarılı ve sürdürülebilir buluyorum. Ayrıca kalite kontrol ekipleri ile yazılım ekipleri arasındaki uçurumu kapatmakta da oldukça işe yarıyor.

Gelelim Jasmine kütüphanesine. BDD bir yöntem veya yaklaşım. Bunun için esasen herhangi bir kütüphaneye ihtiyacımız yok. Aynı Unit Test, Mock, Stub ve IoC için ekstra kütüphanelere ihtiyacımız olmadığı gibi. Ama Amerika'yı baştan keşfetmektense, mavi turun keyfini çıkartmak, hele ki mavi tur ücretsizce, çok daha mantıklı olacaktır. An itibariyle JavaScript'de test yazmak için kullanılan iki popüler kütüphane mevcut. Bunlardan biri QUnit, diğeri de Jasmine. Benim Jasmine'i tercih etmemin sebebi, özellikle BDD düşünülerek yazılmış olması. Gerek notasyonu, gerekse kullanımı BDD ile çok güzel örtüşüyor.

İnşallah bu makale dizisiyle birlikte Open Source / Commercial Free olan Jasmine üzerinden BDD tarzı yazılım geliştirmeyi birlikte öğreniyor olacağız. Konu hakkındaki ilk makalemizi özetlerken, konuştuklarımızı özetleyelim.
  • Legacy Code nedir, artık biliyoruz.
  • Test yazarak, yazdığımız kodun beklendiği gibi çalıştığını garanti ederiz.
  • Testi yazılmış kodu, gelecekte bizim veya başkasının yapacağı değişiklikler sebebiyle iş görmez hale gelme tehlikesinden koruruz.
  • Testini yazarak, yazdığımız kodun başkaları tarafından "Legacy Code" unvanı ile anılmasının ve ilk fırsatta yeniden yazılmasının önüne geçeriz.
  • Klasik yaklaşımlarla, müşterilerin ihtiyaçlarını karşılamayan ve kodun büyük bölümü çalışmadığı halde kurulup destek verilen, ek geliştirmeler sonucu Bug meydana getiren programlar üretiriz.
  • Başlangıçta fazla zaman gider endişesiyle yazılmayan test kodları sebebiyle, sonradan onlarca kat fazla zaman harcanır.
  • BDD yöntemiyle müşterilerin ihtiyaçlarını tam olarak karşılayan, fazladan da ekstra herhangi bir kod bloğunu içermeyen, tüm kullanım durumlarının hatasız çalıştığı test edilmiş programlar üretebiliriz.
  • BDD yöntemiyle yazılmış programlara gelen ek geliştirme taleplerinde tek yapılması gereken, konu ile ilgili testlerin değiştirilerek, yazılım ekibinin bu testleri başarılı hale getirmesine yönelik kodsal çalışma yapmasını istemektir.
  • Jasmine, özellikle BDD yaklaşımı düşünülerek yazılmış Open Source / Commercial Free bir test kütüphanesidir.


Yorum Gönder

 
Top