Test Güdümlü Yazılım Geliştirmeye Merhaba

Test Güdümlü Yazılım Geliştirmeye Merhaba

“Test Driven Development” yani Türkçe söylersem “Test Güdümlü Yazılım Geliştirme” ile birkaç ay öncesinde tanıştım. Fakat bu konuda halen öğrenmeye devam ediyorum. Bu konuda bildiğim temel bilgileri paylaşmak istiyorum. Aynı zamanda bu konuda kendimi geliştirdikten sonra geri dönüp düşüncelerime tekrar bakmak istiyorum. Aslında blog tutmamın en sevdiğim yanı da budur. Yazılım ile ilgili düşüncelerim sürekli değişmektedir. Bu değişimi takip etmenin bir yolu da blog yazmaktır.

Öncelikle yazılım yapmak demek sadece kod yazmak anlamına gelmez. Kod yazmak işin sadece bir kısmıdır. Yazılımda testler de oldukça önemlidir. Çünkü yazdığınız kodların düzgün çalışıp çalışmadığını ancak testler ile anlayabilirsiniz.

Başta ben herşeyin önceden yapıldığını ve kod yazmanın işin hep son aşaması olarak düşünürdüm. Kendimi geliştirmek için yazmaya başladığım projelerde öncelikle detaylı bir tasarım yapardım. İnşaat mühendislerinin yaptığı gibi yazılımı önce çizip tasarlayarak sonra kodlamaya geçerdim. Tasarım prensiplerine hakim olmamamın da etkisi olsa da hep hesaplamadığım şeyler ortaya çıkıyordu. Tasarımım da esnek olmadığı için projeyi kodlarken acayip rahatsızlık hissediyor ve bir sürü kötü kodlama yapıyordum. Bu da beni olağanüstü mutsuz ediyordu. Birşeylerin eksik olduğunu farkettiğimde tasarım ile ilgili dökümanlar aramaya başlamıştım. Bu sayede Özcan Acar ile tanışma fırsatı buldum. KurumsalJava blogunu incelediğimde Özcan Acar’ın test güdümlü geliştirmenin çok önemli olduğunu yazılarında ve seminerlerinde gördüm. Sadece tasarım değil öğrenmem gereken bir sürü şey olduğunu farkettim.

Test güdümlü geliştirme hayat döngüsü Birim testleri için Java da JUnit kullanılıyor. JUnit ile ilk Linux yaz kampında tanışmıştım. Fakat orda test güdümlü yazılım yapmadık sadece yazdığımız kodları test ettik. Test güdümlü yazılım geliştirmek demek sadece yazılan kodları test etmek anlamına gelmez. Yazılımda niyetiniz önemlidir. Kodlamaya başlamadan önce testler yazılır. Daha sonra bu testlerin çalışması için gereken kodlar yazılır. Bu sayede adım adım kodlama yapılmış olur. Bu başlangıçta zaman kaybı gibi gözükebilir. Çünkü yazılımın gereken kodların yanı sıra bir de test yazmak zorundasınız. Bazen iki yazılımcı aynı iş için çalışır. Bir yazılımcı testleri yazarken diğeri de testlerin çalışması için gereken kodları yazar. Test yazmak başlangıçta zaman kaybı olarak görülebilir olsa da aslında durum tam tersidir. Test yazdığınız zaman yazdığınız kodlar %100 test edilmiş olur. Bu sayede yaptığınız yazılım sorunsuz çalışır. Otomatik çalışan testleriniz olmadığı zaman kodları test etmek bir hayli zordur. Bu da zaman kaybettirir. Bunun yanı sıra gereksiz kodlar yazmanızı engeller. Bu sayede yine zaman kazanmış olursunuz. Düşünün bir sınıfınız var ve o sınıfa refactoring uyguladınız veya optimizasyon yaptınız. Yaptığınız değişikliklerin eskisi gibi düzgün çalıştığını nasıl anlarsınız? Bunun için testleri çalıştırmanız yeterli olacaktır. Yazılımınız daha tutarlı bir hale gelmiş olacaktır. Özellikle refactoring yapmak için otomatik çalışan testlerinizin olması şarttır.

Paul Graham’ın “Hackers and Painters” kitabından bir alıntı yapmak istiyorum: “Programlar yazılırken şekillendirilmelidir; tıpkı yazarlar, ressamlar ve mimarların yaptığı gibi.”. Bu söze kesinlikle katılıyorum. Programları yazarken şekillendirmek için ise öncelikle amacımızın yani niyetimizin belirli olması gerekir. Test güdümlü yazılım geliştirmek aynı zamanda bunu sağlamış olur.

Test güdümlü yazılım ile ilgili bildiklerimi aktardıktan sonra bir örnek vermek istiyorum. Çok sevdiğim bir kod katası olan Roman Rakamları katasından örnek vereceğim. Dilerseniz http://kodkata.com/roma-rakamlari-katasi/ adresinden katayı izleyebilirsiniz. Kata hakkında bilgi almak için ise buraya tıklayabilirsiniz.

Öncelikle RomanNumeral sınıfı için test sınıfı (RomanNumeralTest) oluşturuyoruz ve bir test metodu yazıyoruz.

Anlaşılacağı gibi “I” değerini verdiğimde sonuç olarak 1 değerini döndermesi gerekir. Bunu için assertThat metodunu kullandım. İlk parametre olarak test etmek istediğimiz metodu, ikinci parametre olarak beklediğimiz değeri giriyoruz. Burda kodun okunmasını kolaylaştırmak amacı ile CoreMatchers.is ve CoreMatchers.equalTo statik metodlarını kullandım.

Henüz ortada RomanNumeral adlı sınıfımız yok. Öncelikle bu sınıfı oluşturup içerisine convert metodunu oluşturuyoruz. Test kodlarını çalıştırdığımızda başarısız olduğunu görüyoruz. Bu testi geçmek için çok detaylı diğer roman rakamlarını da düşünerek kod yazmıyoruz. Sadece bu testin gerçekleşmesi için gereken kodu yazıyoruz.

return 1 ifadesini eklemek testlerin başarılı olmasını sağlayacaktır. Bunu gerçekleştirdikten sonra 2. bir test daha ekliyoruz.

Burda dikkat ederseniz roman nesnesini yerel değişken olarak kullanmak yerine sınıf değişkeni yani “field” olarak kullanıyorum. Çünkü aynı kodu her test için yazmak zorunda değiliz. Bu sayede DRY(Don’t Repeat Yourself) prensibine uymuş oluyoruz. 2. testimizde gördüğünüz gibi “V” değerini gönderdiğimizde 5 değerini almak istiyoruz. Yukarda bahsettiğim gibi öncelikle niyetimizi yazıyoruz. convert metodunda değişiklik yaparken bu sefer her 2 testin de çalışmasına dikkat etmemiz gerekiyor. O yüzden RomanNumeral sınıfımız aşağıdaki gibi olacaktır.

metodumuza bir if ekleyerek 2 testin de başarılı olmasını sağladık. Aynı şeyi “X” -> 10 için de yapıyoruz.

3. testimizi yazıp gereken değişiklikleri yaptık. convert metoduna baktığımızda istenmeyen bir durum görüyoruz. Her yeni test için sürekli bir if ekleyemeyiz. O yüzden RomanNumeral sınıfını Map kullanarak aşağıdaki şekilde düzenliyoruz. Böylece metodu tek satıra düşürebiliriz.

Burada adım adım giderek refactoring işlemlerini de adım adım yaptık. Test güdümlü geliştirdiğimiz için testlerden yola çıkarak geliştirdik. Katanın devamı için video‘yu izleyebilirsiniz..

test junit refactoring clean code test driven development tdd