Tag Archives: TDD

Detayları Görebilmek Ya Da Görememek

Robert Martin Transformation Priority Premise – Transformasyon Öncelik Teorisi başlıklı yazısında yeniden yapılandırmaya (refactoring) karşı yöntem olan kod transformasyonuna değiniyor. Refactoring uygulamanın dışa gösterdiği davranış biçimini değiştirmeden kodun yeniden yapılandırılması anlamına gelirken, kod transformasyonu uygulamanın davranış biçimini yeniden şekillendirmek için kullanılan bir yöntemdir.

Benim kod transformasyonlarını bilinçli bir şekilde algılamam kod kataları yapmaya başladığım zamana denk gelir. Daha önce de test güdümlü yazılım yaparken kod transformasyonlarına şahit olmuştum, lakin kod kataları ve Robert Martin’in yazısı aracılığı ile transformasyon sürecinin nasıl işlediğini daha iyi algılama fırsatı buldum.

Bu konuda lafı fazla uzatmadan kod transformasyonunun ne olduğunu bir örnek üzerinden sizlerle paylaşmak istiyorum. Aşağıda yer alan kod prime factor katasından alıntıdır. Bu kata bünyesinde herhangi bir rakamın asal sayı (prime number) olan faktörleri hesaplanmaktadır. Bir asal sayı sadece bire ve kendisine bölünebilir. 2,3,5,7,11,13 asal sayılardır. Örneğin 10 rakamı 2 ve 5 asal sayı faktörlerinden oluşur. 6 rakamının asal sayı faktörleri 2 ve 3’dür. Kata bünyesinde oluşturulan generate() metoduna kod transformasyonları aracılığı ile herhangi bir rakamı oluşturan asal sayı faktör listesini oluşturabilecek kabiliyet kazandırılır. Kata için oluşturduğum birim testi aşağıda yer almaktadır:

[source language=’java’]
package com.kodkata.kata.primefactor.kata;

import static com.kodkata.kata.primefactor.kata.PrimeFactorz.generate;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

@RunWith(Parameterized.class)
public class PrimeFactorTest {

private List list;
private int expected;

public PrimeFactorTest(List list, int expected) {
this.list = list;
this.expected = expected;
}

private static List list(Integer… integers) {
return Arrays.asList(integers);
}

@Test
public void generate_as_expected() throws Exception {
assertEquals(list, generate(expected));
}

@Parameters
public static Collection testData() throws Exception {

return asList(new Object[][] { { list(), 1 }, { list(2), 2 },
{ list(3), 3 }, { list(2, 2), 4 }, { list(5), 5 },
{ list(2, 3), 6 }, { list(7), 7 }, { list(2, 2, 2), 8 },
{ list(3, 3), 9 }, { list(2, 5), 10 }, });
}
}

[/source]

Bu parametrik JUnit test sınıfı testData() bünyesinde yer alan veri listesini kullanarak, generate_as_expected() test metodunu koşturmaktadır.

İlk birim testi { list(), 1 } veri kümesi ile 1 rakamı için yapılmaktadır. 1 rakamının asal sayı faktörü yoktur, yani generate() metodu boş bir listesi geri döndürmelidir. Bu testi oluşturduktan sonra aşağıdaki şekilde generate metodunu oluşturuyorum.

[source language=’java’]
public static List generate(int i) {
return null;
}
[/source]

Burada ilk kod transformasyonunu görmek mümkün. Ortada hiçbir kod yokken, bir metot oluşturdum ve metodu null değerini geri döndürecek şekilde yapılandırdım. Robert Martin atılan bu ilk adımı ( {}–>nil ) şeklinde tanımlıyor.

Testin geçebilmesi için kodu aşağıdaki şekilde dönüştürmem (transform) gerekiyor:

[source language=’java’]
public static List generate(int i) {
return Collections.EMPTY_LIST;
}
[/source]

Bu dönüşüm ( nil->constant ) şeklinde ifade edilebilir. Birinci testin geçebilmesi için yapmamız gereken sadece boş bir listeyi (constant) geri döndürmektir.

İkinci test için { list(2), 2 } veri kümesini kullanıyorum. generate() metodu 2 rakamını parametre olarak aldığında, bünyesinde 2 rakamı olan bir listeyi geri döndürmeli. Bu testin geçebilmesi için kodu aşagıdaki şekilde dönüştürmem gerekiyor.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
if (i > 1)
primes.add(2);
return primes;
}
[/source]

Bu dönüşüm ( unconditional->if ) şeklinde ifade edilebilir. Testin geçebilmesi için en basit olan çözümü seçtim. Bu durumda en basit çözüm if kullanarak 2 rakamını listeye eklemek. Tetiklediğim iki kod transformasyonu iki testin geçebilecek seviyeye gelmesini sağladı.

Üçüncü test için { list(3), 3 } veri kümesini kullanıyorum. generate() metodu 3 rakamını parametre olarak aldığında, bünyesinde 3 rakamı olan bir listeyi geri döndürmeli. Bu testin geçebilmesi için kodu aşağıdaki şekilde dönüştürmem gerekiyor.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
if (i > 1)
primes.add(i);
return primes;
}
[/source]

Bu dönüşüm ( constant->scalar ) şeklinde ifade edilebilir. Testin geçebilmesi için bir constant olan 2 rakamını i (scalar) parametresi ile değiştirdim.

Dördüncü test için { list(2,2), 4 } veri kümesini kullaniyorum. generate() metodu 4 rakamını parametre olarak aldığında, bünyesinde 2,2 rakamları olan bir listeyi geri döndürmeli. Bu testin geçebilmesi için kodu aşağıdaki şekilde dönüştürmem gerekiyor.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
if (i > 1) {
if (i % 2 == 0) {
primes.add(2);
i /= 2;
}
if (i > 1)
primes.add(i);
}
return primes;
}
[/source]

Bu dönüşüm ( unconditional->if ) şeklinde ifade edilebilir. Testin geçebilmesi için yeni bir if bloğu içinde ikinin katlarını bulan modülo işlemini gerçekleştirdim. i/=2 ile i’nin değerini ikiye bölmüş oldum. Bunun yanısıra ikinci bir if bloğu içinde arta kalan değeri birden büyük ise listeye ekledim.

5,6 ve 7 rakamları için generate() metodunda değişiklik yapmaya gerek yok. generate() metodu bu rakamlar için geçerli listeleri geri döndürüyor.

8 rakamının asal sayı faktörlerini bulmak için { list(2,2,2), 8} veri kümesini kullanıyorum. Bu testin geçebilmesi için kodu aşağıdaki şekilde dönüştürmem gerekiyor.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
if (i > 1) {
while (i % 2 == 0) {
primes.add(2);
i /= 2;
}
if (i > 1)
primes.add(i);
}
return primes;
}
[/source]

Bu dönüşüm ( if->while ) şeklinde ifade edilebilir. Testin geçebilmesi için ikinci if bloğunu while döngüsüne dönüştürmem yeterli oldu.

9 rakamının asal sayı faktörlerini bulmak için { list(3,3), 9} veri kümesini kullanıyorum. Bu testin geçebilmesi için kodu aşağıdaki şekilde dönüştürmem gerekiyor.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
int divisor = 2;
while (i > 1) {
while (i % divisor == 0) {
primes.add(divisor);
i /= divisor;
}
divisor++;
}
return primes;
}
[/source]

Bu dönüşüm ( if->while ) ve ( constant->scalar ) şeklinde ifade edilebilir. Divisör (bölen) olarak kullandığım 2 rakamını bir değişkene ( constant->scalar ), ilk if bloğunu bir while döngüsüne dönüştürdüm.

generate() metodu bu son transformasyon ile herhangi bir rakamın asal sayı faktörlerini hesaplayacak duruma gelmiş oldu. Yapabileceğim iki transformasyon kaldı. Bu transformasyonlar ile satır sayısını azaltabilirim. Bu iki tranformasyonu ( while->for ) olarak tanımlayabiliriz.

[source language=’java’]
public static List generate(int i) {
List primes = new ArrayList();
for (int divisor = 2; i > 1; divisor++) {
for (; i % divisor == 0; i /= divisor)
primes.add(divisor);
}
return primes;
}
[/source]

Görüldüğü gibi testler geçtikten sonra metodun davranış biçimini kod transformasyonları uygulayarak yeniden yapılandırmamız mümkün. Uyguladığımız her transformasyon ile oluşturduğumuz çözüm spesifik olmaktan çıkıp umumi olmaya doğru gidiyor.

Test güdümlü çalışmayan bir programcı doğrudan generate() metodunu yukarda gördüğünüz şekilde kodlayacak ve mevcut kod transformasyonlarından bihaber olacaktır. Bu transformasyonları görmek bana ne fayda sağlar diye bir soru aklınıza gelebilir. Bilmiyorum! Belki bir faydası yok. Ama ben artık işin daha derinlerinde başka şeylerin varlığını hissetmeye başladım. Bu bana programcılıkta başka bir boyuta geçmek ya da olup bitenleri daha iyi algılamak için kullanabileceğim bir kapı olabilir gibi geliyor. Bazı şeyler detaylarda gizli. Onları görebilmek lazım.

EOF (End Of Fun)
Özcan Acar

Bugün Neler Ögrendim?

Bugünkü günüm bir tweet göndererek başladı. Saat sekiz gibi Frankfurt’ta yapılan Code Retreat etkinliğine katıldım. Code Retreat 15-20 arası programcının bir araya geldiğı ve birlikte gün boyunca kod geliştirdikleri bir etkinlik.

Etkinliğin formatı kısaca şöyle: Etkinlik 45 dakikalık, iki programcının bir araya gelerek eşli programlama (pair programming) yaptıkları altı oturumdan ve her oturum sonunda beş ya da on dakika retrospective ismi verilen, oturum esnasında olup bitenlerin analiz edildiği bölümden oluşuyor. Bir oturum esnasında iki programcı test güdümlü yazılım metodunu kullanarak Game Of Life ismi verilen programı geliştiriyorlar. İlk oturumdan sonra aslında oturum için öngörülen 45 dakikanın böyle bir program yazmak için yeterli olmadığını, ama asıl amacın zaten programı sonlandırmak değil, her oturumda yeni fikirler geliştirerek, başka bir bakış açısından kod yazmaya çalışmak olduğu anlaşılıyor.

İlk oturumda daha önce böyle bir etkinliğe katılmış ve Game Of Life programını tanıyan bir programcı ile eşleştim. Game Of Life hakkında ön bilgim olmadığı için başlangıçta zorlandım. Bunun yanısıra eşimin kodlama esnasında oluşabilecek hataları daha önceden bilerek, beni belli bir yöne doğru itmeye çalışması, oturumu benim için daha zor bir hale getirdi. Ben kafamda Game of Life dünyasını nesnelerle modellemeye çalışırken, birden kendimi eşimin etkisiyle prosedürel bir dünyada buldum. İçgüdüsel olarak bir şeylerin doğru gitmediğini sessiz ve sesli düşünmeye başladım. Oluşan prosedürel bir yapıda benim fikirlerimi nesnelere dökmem imkansız hale geldi. Bu durum beni bloke etti. Ben böyle bocalarken ilk 45 dakika doldu ve ilk retrospective seansına geçtik. Retrospective seansında bunu dile getirdim.

İkinci seansta başka bir programcı ile bir araya gelerek, seansın başlangıcından itibaren Game Of Life oyununu Java sınıfları kullanarak tasarladık. Yazdığımız testler yavaş yavaş sınıflar için gerekli davranış biçimlerini (metot) şekillendirmeye başladı. O test senin, bu kod benim derken ikinci seansında sonuna geldik. Kısa bir retrospektive seansı ardından üçüncü ve diğer seansları tamamladık. Her seansın sonunda yazdığım kodun daha okunur, daha iyi tasarlanmış hale geldiğine sahit oldum.

Her seans sonunda kodları silerek yeniden başlamamız istendi. Bu şekilde yazılan koda ne kadar sahip çıktığımı ve kod silmenin benim için ne kadar zor olduğunu gördüm. Yeni bir perspektif kazanmak için kodun silinmesi gerektiğini anladım. Mevcut bir kodu yeniden yapılandırmak mümkün, lakin çok daha değişik bir bakış açısı ile kod yazmak için kodun silinmesi gerekiyor.

Toplamda bu etkinlik benim için çok verimli geçti. Diğer programcılardan yeni teknikler öğrenme fırsatı buldum. Özellikle eşleştiğim bazı programcıların kullandıkları implementasyon şablonları benim için öğrentici nitelikteydi. Hemen aklıma Kent Beck’in yazdığı Implementation Patterns isimli kitap geldi. Bu kitaba daha önce göz atmış olmama rağmen, şimdi daha başka bir motivasyonla bu kitabı okuyacağım.

Biz programcıların pratik yapması gerekliliğine daha önceki bir yazımda değinmiştim. Bunun ne kadar gerekligi oldugunu bugün daha iyi anlama fırsatı yakaladım. Müzisyenler gibi pratik ve performans arasında ayrım yapmamız ve bu ikisini karıştırmamız gerekli. Ne kadar çok bilinçli pratik yapma fırsati yakalarsak, o oranda programcı olarak daha ileriye gidebileceğimizi düşünüyorum.

Pazartesi Brutal Refactoring Game için Nürnberg’te olacağım. Şimdiden bu etkinliği iple çekiyorum.

EOF (End Of Fun)
Özcan Acar

Ultra Lüks

Biliyorsunuz yazılim sektörü sadece biz yazılımcılardan oluşmuyor. Bu sektörün proje yöneticisi, satış elemanı, testcisi gibi emekçileri de var. Bu meslek gurupları içinde teknik anlamda yazılımın nasıl yapılması gerektiğini bilen ya da bilmesi gereken biz yazılımcılarız. Bu sektördeki diğer meslek guruplarından bu konuda bir ümit beklemeyin.

Durum böyle olmasına rağmen, bir yazılım projesinin nasıl yürütülmesi gerektiği konusunda en az söz sahibi olan biz programcılarız. Bize gelene kadar birçok insan köklü bilgi sahibi olmadan yazılım projelerine yön veriyor, vermeye çalışıyor. Biz programcılar için yazılım kalitesi ön planda iken, ön planda olmalıyken, bu kişiler çok daha değişik parametrelerin gravitasyonuna kapılıp, açıkçası işi berbat ediyorlar. Bu şahısları motive eden en önemli faktör: “ürünü bir an önce satmak”.

Bir ürünü çabucak satmanın önündeki en büyük engel nedir? Cevap: zaman kaybı olarak algılanan aktiviteler. Hangi aktiviteler örneğin? Cevap: birim testleri yazmak, birlikte kod inceleme seansları yapmak (peer code review), sürekli entegrasyon vs. İnanmıyorum dercesine kafanızı sağa, sola salladığınızı görür gibiyim. Benim gibi bunların yazılım kalitesi açısında önemli aktiviteler olduğunu düşünüyorsunuz değil mi? Ama programcılar haricindeki diğer yazılımla ilgili meslek guruplarında böyle bir algılama ne yazık ki mevcut değil. Onlar için bu saydıklarım lüks, biz programcılar ise büyümeyi becerememiş ve istekleri bitmeyen şımarık çocuklarız.

Ellerinden gelse bizi hemen kapıya koyacaklar ama göbekten bize bağlı olduklarını bildiklerinden sesleri çıkmıyor. Bizim de sesimiz çıkmıyor. Arkadaş, yukarda saydığım aktivitelerin gerekliliğine inanmış, ama uygulanmadıkları yerde masaya yumruğunu vurup, “savulun ulan, biz bu aktiviteleri yapacağız, işinize gelirse“ diyen cesur bir programcı görmedim. Ağlamayan bebeğe kim emzik verir ki? Bize bu şekilde davranmaları ve anormal şartlarda çalıştırmaları normal!

Yukarda saymış olduğum aktiviteler modern yazılım pratiklerinden bazılarıdır. Nasıl bir duvar ustasının spatula, fırça gibi alet, edevatı olmadan çalışması zorsa, biz programcıların da bu pratikleri uygulamadan anlamlı sonuç üretmesi zordur. Ha, bu söylediklerim zaten günü kurtarmaya çalışanlara değil! Ben yazılım yapmayı bir bilim ve mühendislik dalı olarak görenler adına konuşuyorum.

Saydığım aktivitelerin lüks olarak görülmesini engelleyememişken, test güdümlü yazılım gibi aktiviteler nasıl algılanıyor sizce? Cevap: ultra lüks! Adam kafadan “iki programcı neden aynı işi yapsın ki, boşuna kaynak harcamam” diyor. Bak! Bak! Arkadaşım sana iki çift sözüm var: Birincisi biz kaynak değiliz, insan evladıyız, ikincisi şimdi sana test güdümlü yazılımın faydalarını anlatırdım, ama anlama kapasiten var mı bilmiyorum.

Zor! Gerçekten bu şartlarda, bu köhnemiş kafalarla beraber çalışmak çok zor. Bir dünya hayal ediyorum, içinde profesyonel programcıların mutlu, mesut oldukları, test güdümlü yazılım gibi pratiklerin ultra lüks değil de, gerekli oldukları düşünülen, proje gidişatını yetkin yazılımcıların tayin ettiği bir dünya. Ne yazik ki böyle bir dünya yok. Elimizdeki ile yetinmek zorundayız. Ama bu ara sıra masaya yumruğumuzla vuramayız anlamına gelmiyor.

EOF (End Of Fun)
Özcan Acar

Eşli Programlama, Code Review, Code Retreat ve Adada Yaşayan Programcılar

Bir programcıyı alıp bir adaya koysanız, diğer insan ve progcılarla bağlantı kurmasını engelleseniz, kendi başına çalışmasını ve kendisini geliştirmesini isteseniz, bu programcı programcılık konusunda nasıl bir gelişme sağlardı? Continue reading

Test Güdümlü Yazılımın Tasarım Üzerindeki Etkileri

Yazılımcı olarak çalıştığım projelerde geleneksel ve çevik yazılım süreçleri hakkında tecrübe edinme firsatı buldum. En son kitabım bir çevik süreç olan Extreme Programming hakkındadır. Edindiğim tecrübeler doğrultusunda çevik süreçlerin, klasik yazılım süreçlerine nazaran bakımı ve geliştirilmesi daha kolay yazılım sistemlerinin oluşturulmasında daha avantajlı olduğunu söyleyebilirim.

Bu yazımda sizelere test güdümlü yazılım sürecinin, yazılım tasarımı üzerindeki etkilerini bir örnek üzerinde aktarmak istiyorum. TDD ile birlikte oluşan tasarım, kendiliğinden oluşan birşey değildir. Testler şekil aldıkça, oluşturmak istediğimiz tasarımın modeli de gözümüzde canlanmaya başlar. Oluşturduğumuz testler, programın gelecekteki kullanıcılarını (client) simule ettiği için, programın nasıl kullanılacağını testler bünyesinde gözlemlemek kolaylaşmaktadır. Bu süreç, sınıfların ve metotların kullanıcı gözüyle (client) tasarlanmasını sağlar. Bu sayede basit ve kullanışlı API (Application Programming Interface)’ler oluşur. Test güdümlü yazılım tasarımı devamlı zorlar ve yetersiz kaldığı yerlerde refactoring yöntemleriyle yenilenmesini sağlar. Bu süreç sayesinde kendisini devamlı yenileyen ve yeni gereksinimlere cevap veren bir tasarım oluşur.

Bu yazıyı PDF olarak edinebilirsiniz.


EOF (End of Fun)
Özcan Acar

Test Güdümlü Yazılım – Test Driven Development (TDD)

 

Günümüzde kurumsal projelerin büyük bir bölümü geleneksel yazılım  metotları ile gerçekleştirilmektedir. Müşteri gereksinimleri en son detayına kadar kağıda döküldükten sonra, programcılar dokumente edilen gereksinimler doğrultusunda yazılımı gerçekleştirmektedirler. Eğer proje bütçesi yeterli ise, yazılım süreci sona erdikten sonra testler hazırlanarak, yazılım sistemi test edilmektedir. çoğu zaman hiçbir unit testin yapılmadığı sistemlerin firmalar tarafından kritik iş alanlarında kullanıldığını görmek mümkündür. Bu tür yazılım sistemlerinde oluşan hatalar (Bug)  firmanın sunduğu hizmetleri kısıtlamakta ve en kötü ihtimalle firmanın para kaybetmesine sebep olmaktadır.

Geleneksel tarzda oluşturulan yazılım sistemlerinde oluşan hataları gidermek çok pahalıya mal olabilmektedir, çünkü yazılım bittikten sonra tespit edilen hatalar yazılım sistemindeki tasarım açıklarını gözler önüne serebilir ve bu gibi kardinal hataların ortadan kaldırılması ya imkansız yada çok zor olabilir. Bunun yanı sıra yazılım sona erdikten sonra oluşturulan testlerin test kapsama alanı geniş olmadığı için kodun bazı bölümleri test edilememekte ve böylece hata tespiti zorlaşmaktadır. Bu şekilde yazılım esnasında ortaya çıkmayan hatalar, daha sonra sistem kullanıcıları tarafından keşfedilmektedirler. Bu aşamada geç kalınmıştır: ya sistem çalışmaz durumdadır, yada sistem kullanıcısı istediği işlemi doğru olarak gerçekleştirememiştir. Durumu kısaca şöyle özetleyebiliriz: “yazılım sistemi müşterinin gereksinimlerini tatmin edecek kaliteye sahip değildir”.

Ne yazık ki birçok firma için kullandıkları yazılım sistemlerindeki kalite problemleri firmaya zarar verici durumdadır. Bu kalite problemleri bir taraftan oluşan sistem hataları, diğer taraftan kodun bakımı ve geliştirilmesinin zor olmasından kaynaklanmaktadır. Oluşan sistem hataları  firmanın giderlerini artırmakta ve yazılım sisteminin istikrarsız ve güvenilmez olmasına sebep vermektedir. Bu sorunların temelinde test konseptlerinin bir yazılım sistemi için hayat sigortası olduğunun anlaşılamamış olması yatmaktadır.
 

Bu yazıyı PDF olarak edinebilirsiniz.