Test Yazarken Neden Mutsuzuz?
You can read this article in English here.
Güneşli bir günden herkese merhaba :)
Bugün sizlerle uzun zamandır üzerinde yoğunlaştığım, sizlerin de zaman zaman kendinizle tartıştığınıza inandığım bir konu hakkında konuşacağız. Başlıktan da anlaşılacağı üzere “Unit Test nedir?”, “Unit Test Neden Önemlidir?”, “Code Coverage Nasıl Arttırılır?” ya da “Ne Tür Test Yöntemleri Vardır?” gibi üzerinde çokça okumalar yaptığımız sorulardan uzaklaşıp, konunun daha derinine inerek “Neden” sorularına cevap arıyor olacağız.
Konuyu detaylandırmadan önce bu soruların neler olduğuna bir göz atalım.
- Neden test yazmıyoruz?
- Neden test yazamıyoruz?
- Neden test yazmayı bırakıyoruz?
- Neden test yazmak zor geliyor?
- Neden test yazarken mutsuzuz?
- …
Buradaki soruların sayısını daha da arttırabiliriz. Ancak her ne kadar daha çok soru üretsek bile, tüm soruların birbirleriyle ilişkili olduğunu ve aynı noktayı vurguladıklarını görüyor olacağız.
Gerçekler Beklediğimiz Gibi Mi?
İçinde bulunduğumuz ekip ve ürüne göre zaman zaman günlük yaptığımız işler değişse de, günün sonunda canlıdaki ürünü geliştirmeye ve ürünle ilgili problemleri çözmeye devam ediyoruz.
İdeal dünyada yazılım geliştiriciler olarak, kahve eşliğinde gerekli geliştirmeleri yapıp, bu geliştirmeleri canlıya alıp ve gerekli yeni düzenlemeleri yaparak (refactoring) görseldeki gibi günü tamamlamak isteriz fakat gerçek dünyada işler bu şekilde ilerleyemeyebiliyor :)
Canlıdaki sistemimizde aniden çeşitli hatalarla karşılaşabilir ve müşterilerimiz bu hatalara maruz kalabilir. Hatta, müşterilerin hatalara maruz kalma süreci arttıkça bize maddi ve manevi anlamda kötü sonuçlara sebep olacaktır. Bu noktada, günümüzün belki büyük bir çoğunluğu önce hataların sebebini bulup anlamakla ve sonrasında çözmekle geçiyor olacaktır.
Neyse ki kısa bir süre içinde sorunu tespit ettik ve gerekli düzeltmeleri yaparak canlı ortama yeni bir paket çıktık. Tam rahat bir nefes alıyoruz derken tekrardan hata sayıları yükselmeye başladı. Acaba yaptığımız değişiklik yüzünden mi böyle oldu yoksa yeni bir hata ile mi karşılaşıyoruz diye düşünürken, artık o çıkmaz labirentin içinde kaybolduğumuzu hepimiz farketmişizdir. İşler daha da kötüleşmeye başlıyor, değil mi?
Tam da bu noktada, “Yani, test yazsaydık bunlar olmayacaktı. Test yazmak önemli. Bunu zaten biliyoruz..” dediğinizi duyar gibiyim. Haklı olmanızla beraber burada önemle bilinmesi gereken şey: test yazmamıza rağmen bu süreci yine yaşıyor olabilirdik (örn: yanlış test tasarımı, soyutlama hataları, vb.). Şuan ki odağımız bu olmadığı için farklı bir yazıda tekrardan tartışıyor olacağız.
Deadline Geliyor!
Biraz daha bilindik bir sorunla başlayacak olursak, deadline — yapılan geliştirmenin tamamlanması için belirtilen son tarih — ürün ekibiyle yazılım ekibi arasında pimi çekilmiş el bombası gibidir örneği kısmen durumun önemini anlatıyor olacaktır.
Ürün ile ilgili herhangi bir geliştirme yaparken işin biteceği süre için bir tarih sözü veririz ve bu sözü de gerçekleştirmeye çalışırız. Ancak, bazı durumlarda belirtilen zamana işler yetişemeyebilir. İşlerin neden yetişemeyeceğiyle ilgili sayısız sebep sayabiliriz. Burada kritik nokta, “İşi yetiştirmemiz gerekiyor” noktasına odaklanarak test kavramını unutuyor oluşumuz.
Yapılan bir işin (örn: yeni bir geliştirme, hata ayıklama, kodu yeniden düzenleme, vb.) uçtan uca tamamlanmasında testin bu sürecin bir parçası olduğunu, işlere süre tanımlarken testi de içine katarak süre hesaplamayı ve baskı hissederek testin ekarte edilebilir olmaması gerektiğini özümsememiz gerekiyor.
Kimse Zaten Test Yazmıyor!
Bu başlık nadir de olsa (umarım öyledir) karşımıza çıkan sorunlardan biri olarak yorumlanabilir. Burada konu teknik bir noktadan dışarı çıkıp, daha düşüncesel ve insani bir noktaya evriliyor.
“Başkası zaten test yazmıyor” şeklinde sorgulamanın kendimce doğru olmadığını belirtmek isterim. Çünkü, bu sorgulamanın “başkası zaten interface kullanmıyor” ile aynı olduğu düşüncesindeyim. Başka biri yapmıyor diye bu sizin yapmanıza ya da siz yapmıyorsunuz diye başkasının da yapmamasına engel değildir.
Bu başlık altında değinmemiz gereken en önemli nokta, takım/şirket içinde test kültürünün oluşturulması ve o kültürün içeride dağıtılmasıdır. Böyle bir ortam sağladığında yukarıda bahsettiklerimizin aksine, birbirini test yazmaya yöneltecek ve destekleyecek yapılaşmalar oluşacaktır.
Kod Aşırı Karmaşık ve Dokunulmaz Durumda!
Geldik en önemli başlığa! Bu başlık altında konuşacaklarımızın, “Neden” sorularımıza en çok dokunan nokta ve üzerine hep düşünmemiz, asla unutmamız gereken bilgiler olduğuna inanıyorum.
Hepimiz belki çoğu zaman belki daha nadir de olsa yönetilmesi zor, gelişime kapalı, değiştirilmesi neredeyse imkansız olan projelerle karşılaşmışızdır. Bırakın test yazmayı, kod yazmak, geliştirme yapmak bile hayal desek yeri vardır. İşte bizleri testten soğutan ya da istesek bile test yazmamıza engel olan şey, bu tür projelerin sürdürülebilirliğini devam ettirmektir.
Peki neden bu tarz kod yapılarıyla ya da bu şekilde yazılmış projelerle karşı karşıya kalıyoruz? Bu soruya hepimizin verebileceği çeşitli cevaplar olduğuna eminim. Fakat, bakış açığımızı biraz daha değiştirip, işin daha özüne inelim ve Jack Revees üstadın “What is Software Design?” yazısındaki bir bölüme dikkat çekelim.
“Coding is design, testing and debugging are part of design.” Jack W. Revees
“Kodlamak bir tasarım işidir, test etmek ve hata ayıklamak bu tasarımın bir parçasıdır.” derken ne demek isteniyor? Hani bizim önümüze iş geliyordu ve biz de projeyi açıp, Repository’ler ile veritabanı üzerinde CRUD yapıp, sonrasında Manager’lar üzerinden iş geliştirmiyor muyduk? Nerden çıktı bu tasarım kavramı diyor olabilirsiniz.
Yazılım geliştirmek sadece problem çözmek ve bu problemi çözmek için kodlama yapmak değildir. Problem çözmek işimizin sadece bir parçasıdır. Bu problemi bütünüyle nasıl çözeceğimizi ele almak, bu çözüm yolunu geliştirmek asıl problemdir. Tam bu noktada, “Nasıl” dediğimiz sorunun cevabı tasarım olacaktır. Tasarlamak bir plan işidir. Yani, elinizdekileri doğru şekilde kullanarak — doğru planlayarak — sorun çözme yöntemidir.
Yazılım açısından tasarım kavramına baktığımız zaman, kaynak kodumuz (source code) bizim gerçek tasarımımızdır ve gelişime açık, esnek, sürdürülebilir bir kod için iyi bir tasarıma ihtiyacımız olacaktır. Bu tasarımı elde edebilmek için soyutlama (abstraction), bağımlılık (coupling), bağlılık/bütünlük (cohesion) gibi kavramları gözden geçirmemiz gerekiyor.
Şimdi işin en güzel kısmına doğru geliyoruz. Bu bahsettiğimiz tasarım (yani kodumuz) test (testing) ve hata ayıklama (debugging) olmadan olamaz. Diğer bir deyiş ile, bu ikili olmadan tasarım tamamlanmış sayılamaz. Tam bu noktada, kod ile test arasında bir bağın kurulduğunu görebiliriz.
Son olarak hata ayıklamadan bahsetmiştik. Elbette geliştirdiğimiz kodlar içerisinde hatalar olacaktır. Önemli olan nokta, bu hataları hızlı bir şekilde bulup, düzeltebilmektir. Refactoring bu yüzden kodlama ve test arasında ikisini birbirine bağlayan bir köprü görevindedir. Sadece yazdığımız kodların değil, testlerin de gerektiği durumda refactor edilmesi gerektiğini, refactoring kavramının hem koda hem teste dokunduğunu unutmamak gerekiyor.
Kod ve test bir bütündür ve ayrı düşünülemez! Bütün problemlerin bu yüzden başladığını unutmamamız gerekiyor. Yani, kod karmaşıklığını ve geliştirilemez kod yapılarını ortadan kaldırmak için, iyi ve doğru bir tasarıma ihtiyacımız olduğunu ve bu tasarımın da test olmadan olmayacağını bilmemiz gerekiyor.
Sonuç Olarak Nasıl Mutlu Olacağız?
Test kavramının önemini ne kadar bilsek de neden test yazamadığımızdan, yazarken hangi zorluklarla karşılaştığımızdan ve bizi soğutan şeylerden detaylıca bahsettik. Bizleri test yazmaya geri döndürecek birçok yöntem sunabiliriz ama tartışmasız en önemlisini Misko Hevery söylüyor.
“The secret of testing is writing testable code.” Misko Hevery
Tüm bu konuşmalarımızı özetleyecek olursak test yazabilmemiz için, test yazarken mutlu olabilmemiz için işi baştan sıkı tutup test edilebilir yapılar geliştirmemiz gerekiyor. Bunun için en önemli ihtiyacın iyi bir tasarıma sahip olmak gerektiğini, tasarımın gerçek kaynak kodumuz olduğunu, bu tasarımın test ve hata ayıklama olmadan bir bütün olamayacağını, refactoring kavramının sadece bir kere değil yazılım geliştirme devam ettiği sürece hayatımızda olacağını ve kod ile test arasında iki tarafa da dokunan konumda olduğunu unutmamalıyız.
Konuyla ilgili Farmazon’da yapmış olduğum daha detaylı sunuma buradan ulaşabilirsiniz.
Eksik veya hatalı bulduğunuz noktaları ya da konuyla ilgili olarak yaşadığınız deneyimlerinizi yorumlar bölümünden belirtirseniz çok sevinirim. Umarım yararlı olmuştur :)