Category Archives: Test Driven Development

Eski Kodu Test Etmek : Subclass and Override

Eğer sıfırdan başlamış bir projede(Green Field) başından beri çalışan şanslı insanlardan değilseniz mutlaka sizden daha önce ve şuanda şirkette olmayan birisinin yazdığı anlaşılamaz kodu değiştirmek zorunda kalmıştırsınız. Benimde başıma sık sık gelen bu tarz durumlarda insan biraz kendini Rus-Ruleti oynar konumunda bulabiliyor. Bir yandan şeytan kodu değiştirip riski göze almanızı söylerken, bir yandan içinizden gelen ses hata yapmamanız için tekrar tekrar kodu kontrol edip hata yapmadığınızdan emin olmanızı söylüyor.

Bu tarz durumlarda eğer değiştireceğiniz kod uygulama açısından kritik bir kod ise onun için Unit Test yazıp kodun şuandaki durumunu test altına alıp ardından değiştirip bir şeyleri bozup bozmadığınızı bu testler ile kontrol etmek gidilmesi gereken en güvenilir yollardan birisi. Tabi bu yöntemde o kadar kolay olmayabiliyor bende her zaman yaptığımı söyleyemem . Dediğim gibi kodu değiştirmek eğer kritik önem taşıyorsa ve risk içeriyorsa elimden geldiğince testle ile kendimi sağlama alıp bu şekilde devam etmeye çalışıyorum.

Bu tarz bir durumda kodu test altına almanın her zaman kolay olmadığını yukarıda belirtmiştim. Çünkü genelde eski kodlar Bağımlılığı oldukça yüksek, testlerden yoksun kodlar olduğu için test altına almak istediğinizde beklemediğiniz engellerle karşılaşabiliyorsunuz.

Aşağıdaki gibi daha önceden yazılmış bizim için kritik önem arzeden ve değiştirmemiz gereken bir kodumuz olduğunu düşünün.

public class PriceCalculator
{
    private Database database;

    public PriceCalculator()
    {
        database =new Database();
    }

    public double CalculateShippingCost(Order order)
    {
        if(order.Total>100)
       {
           return order.Total*GetGoldenOrderTotalRatio();
       }
        
        if(order.Total> && order.Total<100)
        {
            return order.Total*GetBronzeOrderTotalRatio();
        }

        return order.Total*GetNormalOrderTotalRatio();
    }

    private double GetGoldenOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Golden'");
    }

    private double GetBronzeOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Bronze'");
    }

    private double GetNormalOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Normal'");
    }
}

Yukarıdaki gördüğünüz sınıfın bizim için verilen siperişin ulaştırma maliyetini hesaplayan bir sınıf olduğunu düşünün. Burada gördüğünüz gibi siperiş tutarı eğer 100’den büyük ise belirli bir oranda altın indirim yaparak hesaplıyor, 50 ile 100 arasındaysa belirli oranda bronz indirim yapıp hesaplıyor. Bizden yapmamız istenen bu mantığı değiştirip 50 ile 100 aralığı olan bronz indirim mantığını sipariş tutarı 80 ile 100 arasına çekmek.

Burada kodun çok basit olduğunu unutun ve gerçekten çok daha karmaşık hesaplamaların olduğu bir metodu değiştirmek istediğinizi düşünün. Bunu yapmadan önce dediğim gibi bu konu test altına almak oldukça güvenilir yoldan ilerlemenizi sağlayacaktır. Bu yüzden bende öyle yapıp yukarıdaki kod için aşağıdaki gibi testimi yazmaya başlıyorum.

    [TestFixture]
    public class PriceCalculatorTest
    {
        [Test]
        public void CalculateGoldenOrderShippingCost()
        {
            PriceCalculator calculator =new PriceCalculator();
            Order goldenOrder =new Order();
            goldenOrder.Total = 200;

            Assert.AreEqual(10, calculator.CalculateShippingCost(goldenOrder));
        }

        [Test]
        public void CalculateBronzeOrderShippingCost()
        {
            PriceCalculator calculator = new PriceCalculator();
            Order bronze = new Order();
            bronze.Total = 90;

            Assert.AreEqual(9, calculator.CalculateShippingCost(bronze));
        }

        [Test]
        public void CalculateNormalOrderShippingCost()
        {
            PriceCalculator calculator = new PriceCalculator();
            Order normal = new Order();
            normal.Total = 50;

            Assert.AreEqual(7, calculator.CalculateShippingCost(normal));
        }
    }

Yukarıdaki testlerde 3 durumu da test içerisine alacak test kodunu yazdım. Eğer sipariş tutarı 200 ise nakliye oranının %5 olduğunu düşünüp sonucun 10 olması gerektiğini söylüyorum. Eğer tutarı 90 olan bronz sipariş nakliye oranının %10 ise sonucun 9 olması gerektiğini yazıyorum. Ve son olarak da tutar 50 için nakliye oranı %14 ise sonucun 7 olduğunu test eden kodu yazıyorum. Testleri çalıştırdığımda aşağıdaki gibi bir Unit Test hatası karşıma çıkıyor

PriceCalculatorTest.CalculateNormalOrderShippingCost : …AssertionException: Expected: 7 But was: 950.0d

Sebebine baktığımda ise Database üzerinde kayıtlı olan oranların farklı olduğu görüyorum. Bu yüzden beklediğim sonuçlar çıkmıyor. Burada gidip database içerisinden test için değerleri değiştirmek pek mantıklı olmaz.Test kodunu database’deki değerlere göre değiştirmek de pek doğru değil çünkü testimin database’den bağımsız olmasını istiyorum. Başkasının ben farkında olmadan farklı bir değer girdiğinde testlerimin fail etmesini istemiyorum. Yani kısacası burada kısmen gerçek anlamda kontrolün testlerde olmasını her hangi bir dış etkenden etkilenmemesini istiyorum. Daha önceden yazılmış eski kod için bunu yapmanın yollarından biri Subclass and Override verilen teknik. Bunu ilk defa Working Effectively With Legacy Code kitabından öğrenmiştim.  

Bizim burada kontrol etmek istediğimiz aslında kodun biraz kötü yazılmış olmasında kaynaklanan PriceCalculator sınıfının içerisindeki GetGoldenOrderTotalRatio tarzı metodlar. Çünkü bu metodlar direk olarak Database üzerinde sorgu çalıştırarak testlerimizde kontrolün bizden database tarafındaki değerlere geçmesine sebep oluyor. Eğer bunları kontrol altına alırsak testlerimizde istediğimiz sonuçları elde edebiliriz.

Subclass and Override tekniğini basit olarak test için kontrol etmeniz gereken metodu override ederek test ortamında istediğiniz değerleri dönmesini sağlamak oldukça basit ve faydalı bir yöntem. Bu tekniği kullanarak PriceCalculator sınıfımı aşağıdaki gibi değiştiriyorum.

public class PriceCalculator
{
    //Diğer kodlar.....

    protected virtual double GetGoldenOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Golden'");
    }

    protected virtual double GetBronzeOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Bronze'");
    }

    protected virtual double GetNormalOrderTotalRatio()
    {
        return (double)database.ExecuteScalar("SELECT Ratio FROM OrderShippingRatio WHERE Type='Normal'");
    }
}

Gördüğünüz gibi private olaran metodlar protected virtual olarak değiştirdim. Böylece başka bir sınıfı bu sınıftan türetip bu metodları override edebilirim ve istediğim değerleri döndürebilirim. Bu da testlerimde bu metodların database’e çağrı yapmadan benim istediğim değerleri döndürmemi sağlar kısacası kontrol tekrar testlere geçer.Bu arada eğer yukarıdaki kod bir Java kodu olsaydı
Java’nın sevdiğim bir özelliği olan metodlar default olarak virtual olduğu için sadece protected yapmam yeterli olacaktı.Test kodlarımı da aşağıdaki gibi değiştiriyorum.

[TestFixture]
public class PriceCalculatorTest
{
    class TestingPriceCalculator : PriceCalculator
    {
        protected override double GetGoldenOrderTotalRatio()
        {
            return 0.05;
        }

        protected override double GetBronzeOrderTotalRatio()
        {
            return 0.1;
        }

        protected override double GetNormalOrderTotalRatio()
        {
            return 0.12;
        }
    }

    [Test]
    public void CalculateGoldenOrderShippingCost()
    {
        PriceCalculator calculator =new TestingPriceCalculator();
        Order goldenOrder =new Order();
        goldenOrder.Total = 200;

        Assert.AreEqual(10, calculator.CalculateShippingCost(goldenOrder));
    }

    [Test]
    public void CalculateBronzeOrderShippingCost()
    {
        PriceCalculator calculator = new TestingPriceCalculator();
        Order bronze = new Order();
        bronze.Total = 90;

        Assert.AreEqual(9, calculator.CalculateShippingCost(bronze));
    }

    [Test]
    public void CalculateNormalOrderShippingCost()
    {
        PriceCalculator calculator = new TestingPriceCalculator();
        Order normal = new Order();
        normal.Total = 50;

        Assert.AreEqual(6, calculator.CalculateShippingCost(normal));
    }
}

Yukarıdaki test kodlarında gördüğünüz gibi TestingPriceCalculator adından PriceCalculator’dan türeyen bir sınıf oluşturdum ve virtual metodları override ederek istediğim değerlerin dönmesini sağladım. Normalde PriceCalculator olarak test edilen yerleride bu sınıfla değiştirdiğimde test kodlarının istediğim şekilde çalıştığını görüyorum ve testleri çalıştırdığımda geçtiğini görüyorum.

Gördüğünüz gibi varolan kodda çok küçük değişiklikler yaparak kodu Unit test altına aldık. Bu değişiklikler benimde hoşuma gitmesede eski kodlarda kodu test edebilmek için bu tarz değişiklikler yapmak zorunda kalabiliyoruz. Bu kodu başka şekillerde de test edebilirdik bu teknik onlardan sadece biri. Eski kodu test ederken zaman, yapabileceğiniz değişikliklerin kapsamı gibi etkenler oldukça önemlidir. Bu yüzden elinizdeki araçlardan size en uygun olanını seçmek faydanıza olacaktır.

Örnek Kod : Visual Studio Class, Table Generator Add-In

Visual Studio’da Add-In geliştirmeyi öğrenmek için küçük bir proje geliştireyim dedim. Projede basit olarak bir Solution Explorer’dan seçtiğiniz sınıfı Server Explorer üzerindeki veritabanına tablo olarak ekliyor.Ayrıca

Server Explorer üzerindeki eklenmiş veritabanı bağlantısı üzerinden seçtiğiniz tabloyu projenize sınıf olarak ekliyor.

Kaynak kodunu

Hit This dry ed medication matters a keeps Amazon http://www.backrentals.com/shap/cialis-free-samples.html only. Certain looking five http://www.mordellgardens.com/saha/sildenafil-citrate-tablet.html Sure moisturizering my it! Started http://www.creativetours-morocco.com/fers/viagra-from-india.html waterproof. Distribute That’s: the review http://augustasapartments.com/qhio/cialis-online-overnight-shipping went never volume “click here” hilobereans.com eyebrows active when this. Large http://augustasapartments.com/qhio/low-dose-cialis The, and seeing skin, this prescription viagra online inspected to mistake would, http://www.goprorestoration.com/ordering-viagra-online information Thank caramel total! cialis no prescription Hair only and side effects for cialis teddyromano.com managable you provided My usuage buy cialis vermontvocals.org they that usually days.

ve setup dosyalarını aşağıdan indirebilirsiniz. Proje Visual Studio üzerinde Add-In geliştirmek isteyenler için faydalı olabilir. Ayrıca projeyi küçük bir Test Driven Development ve OOP örneği olarak inceleyebilirsiniz yeni başlayanlar için faydalı olabilir..Gerçekte işinize yararsada kullanıp kodu istediğiniz gibi değiştirmekte serbestsiniz.İçerisinde bug,ve eklenmesi gereken özellik olabilir, kendiniz ekleyebilir veya benden eklememi isteyebilirsiniz :) .

Proje Visual Studio 2008 üzerinde çalışıyor. Eğer kodu kullanmak ve değiştirmek istiyorsanız bilgisayarınızda Visual Studio 2008 SDK kurulu olması gerekli.

Tablodan Sınıf Oluşturma

Adım 1 : Server Explorer’dan Çalıştığınız Veritabanı bağlantısını ekleyin ve ekledikten sonra sınıfını oluşturmak istediğiniz tabloyu seçin

TableToClassAdim1

Adım 2 : Gelen ekran üzerinden sınıfın otomatik olarak oluşturulmuş özellikleri üzerinde değişiklik yapın.Sınıf adını değiştirin, yeni özellik ekleyin,özellik silin,özellik adını güncelleyin,özellik tipini güncelleyin…

TableToClassAdim2

Sınıftan Tablo Oluşturun

Adım 1 :Solution Explorer üzerinde veritabanına tablo olarak eklemek istediğiniz sınıfın üzerinde sağ tıklayın.

ClassToTableAdim1

Adım 2: Gelen ekran üzerinden veritabanına eklenecek tablo özelliklerini otomatik olarak görüntüleyecektir. Oradan ekran üzerinde oluşturulacak tablonun primary key, allow null gibi özelliklerini değiştirin, kolon ekleyin, kolon silin, kolon adını değiştirin…

ClassToTableAdim2

TDD : Mocks, Stubs and Two Smoking Barrels

Örnekleri buradan indirebilirsiniz

İçerik

Başlık ne alaka demeyin Test Driven Development’da Mock ve Stub nesnelerin adı bana her zaman çok beğendiğim Lock, Stock and Two Smoking Barrels filminin adını hatırlatmıştır. Bu arada filmin biraz reklamını yapayım :)

“Test Driven Development nedir, ne değildir?”in temel felsefesine daha önceden bu yazıda değinmiştik. Şimdi biraz daha derinlere inip gerçek hayatta TDD ile geliştirdiğimiz yazılımlarda önümüze çıkan problemlere ve onlara nasıl çözüm bulduğumuza bakalım.

Bildiğiniz gibi Unit Test; bir unit’i -genellikle bu bir sınıfdır- test ortamında oluşturup o nesnenin çeşitli metodlarını çağırıp, çeşitli alanlarını değiştirip… kısacası üzerinde işlem yaptıktan sonra ortaya çıkan sonuçların beklediğimiz gibi gerçekleşip gerçekleşmediğni sınadığımız bir test çeşididir. Aşağıda çok basit bir unit test görüyorsunuz.

    [TestFixture]
    public class CalculatorTest
    {
        [Test]
        public void AddTest()
        {
            Assert.AreEqual(5,Calculator.Add(3, 2));
        }
    }
    class Calculator
    {
        public static double Add(double a, int b)
        {
            return a + b;
        }
    }

Test Edilmesi Zor Olan Kodlar

Malesef ne gerçek kodlar bu kadar basit olabiliyor ne de onları test etmek bu kadar kolay olabiliyor. Çünkü gerçek projelerde işin içine Database,Network,Web Servisleri,Dosyalar… gibi birçok dış etken giriyor.Takdir edersinizki object oriented programming, nesnelerin bir araya gelip işbirliği içinde çalışmaları, birbirlerine mesaj gönderip almalarıyla işlevini yerine getirebiliyor. Bu nesneleri test etmek için de test ortamında onların iletişim halinde olduğu diğer nesneleride test ortamına sokmak gerekiyor. Çünkü nesne onlar olmadan çalışmıyor.Fakat bazı nesneleri hem test ortamında oluşturmak zor hem de bu nesnelerin test ortamında istediğimiz gibi davranmasını sağlamak zor.

Bu yüzden bu nesnelerin hem testlerini yazmak, hem de gerçekleyen kodlarını yazmak yukarıdaki gibi basit olmuyor. Bu arada basit olmuyor dediysem gözünüz korkmasın yine basit fakat Assert.AreEquels‘den daha fazlasını yapmak gerekiyor yani kısaca biraz daha uğraştırıcı diyebilirim. Mesela gerçek projelerdeki bir metod aşağıdaki gibi olabiliyor.

        public void ProcessMessage()
        {
            IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999);

            Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                server.Connect(ip);
            }
            catch (SocketException e)
            {
                Console.WriteLine("Unable to connect to server.");
                return;
            }

            byte[] data = new byte[1024];
            int receivedDataLength = server.Receive(data);
            string message = Encoding.ASCII.GetString(data, 0, receivedDataLength);
            
            server.Shutdown(SocketShutdown.Both);
            server.Close(); 

            if(message.Contains("VC"))
            {
                //mesaja göre işlem yap ardından veritabanına kaydet
            }
        }

Peki bu metodu test etmek neden zor? Cevap basit çünkü burda bahsi geçen Database,Network,Web Servisleri gibi ortamların çoğu bizim kontrolümüz dışında.Ayrıca yukarıdaki kodlama kötü bir yazım çünkü mesaj okuma ve onu işleme gibi iki ayrı sorumluluğu yerine getiriyor. Test ortamında istediğimiz zaman onlardan beklediğimiz sonucu elde edemeyebiliyoruz.Mesela yukarıdaki kod bloğu için “VC” içeren mesaj geldiğinde doğru işlemlerin yapılıp yapılmadığını test etmek istediğimizi farzedelim.Ne gibi zorluklarla karşılaşırız? Öncelikle en büyük problem imiz network’ün kontrolünün elimizde olmayışı.Elimizde networkden “VC” mesajının geleceğine dair bir garanti yok.Bu eksikliği şu şekilde giderebiliriz:”Biz testi çalıştırırken eş zamanlı olarak bir arkadaşımızın ayrı bir bilgisayardan bize bir program ile “VC” mesajı göndermesini sağlayabiliriz”.Herhalde bunu arkadaşınızın her test için kabul edeceğini düşünmüyorsunuz.Diğer seçeneğimiz ise test ortamında bu metodu test etmeden önce kod ile bir “Socket Client” oluşturup o mesajı gönderilmesini sağlamak olabilir.Bu kısmi bir çözüm olarak görülse de yapacağımız her test için socket oluşturmak,socket kapatmak için kod yazmak oldukça zahmetli bir iş.

Bu tarz test ortamında oluşturulması zor sınıfları Test Doubles dediğimiz teknikler ile test edebiliyoruz. Şimdi de “Mock” ve “Stub nesneler” ile bu şekilde test edilmesi zor olan sınıfları nasıl test edebileceğimize bakacağız.Her zamanki gibi bir örnekle başlayalım:

Örnek Senaryo : Mail Raporu Oluşturma

Bizden bir mail sunucusundaki belirli bir inbox’a bağlanıp oradaki maillerin kaçının okunup kaçının okunmadığını bulan bir program yazmamız isteniyor. Bu arada inbox bağlantısı için bize kullanıcı adı ve şifre verilmiş, bu bilgileri kullanarak inbox bağlantısını kuruyoruz ve mailleri okuduktan sonra bağlantıyı kapatıyoruz.

Tabi her zamanki gibi Test First Development yapıyoruz.Yukarıdaki gibi bir senaryo için nasıl testler yazabiliriz?Öncelikle metodumuzun doğru çalışıp çalışmadığını anlamak için bir test yazmamız lazım ardından da testi geçecek kodu yazmamız lazım.Testlerde kontrol etmek istediğimiz durumlar aşağıda sıralanmıştır:

  1. Kullanıcı adı şifre ile mail sunucusuna bağlanmadan önce bağlantı açılmış mı?
  2. Mailler alınıp okunmuş/okunmamış mail sayıları düzgün hesaplanmış mı?
  3. Bağlantı kapatılmış mı?.

Bu testlerin sonucunun istediğimiz gibi olup olmadığını nasıl kontrol ederiz?Mesela şöyle bir test ve altındaki gibi de testi geçecek kodu yazdığımızı vasaryalım.

        [Test]
        public void MailRaporuOlustur()
        {
            MailRaporuOlusturucu mailRaporuOlusturucu =new MailRaporuOlusturucu ();
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153,rapor.OkunmusMailSayisi);
            Assert.AreEqual(5,rapor.OkunmamisMailSayisi);
        }
    public class MailRaporuOlusturucu 
    {
        public const string KULLANICI_ADI = "Cihat";
        public const string SIFRE = "1111";
        public MailRaporu RaporOlustur()
        {         
	        //Sunucuya kullanıcı adı şifre kullanarak bağlantı açma 
            //Gerçek mail sunucusuna bağlanıp mailler alınıyor sayılarını hesaplıyor
            //Bağlantıyı kapatılıyor
            return new MailRaporu(okunmusMailSayisi,OkunmamisMailSayisi);
        }
    }

Şimdi yukarıdaki testin ve kodun problemleri neler onlara bakalım.

Öncelikle test içinde okunmuş mail sayısının 153 okunmamış mail sayısınında 5 olduğunu varsayıyoruz. Peki bunun bir garantisini verebilirmiyiz?Ya bağlandığımız gerçek mail kutusunda 200 tane okunmuş mail varsa ya hiç okunmamış mail yoksa?Bu yüzden bu metodu doğru test edemiyoruz çünkü gerçek mail sunucusu kontrolümüz altında değil hatta belki testin çalıştığı anda ortada bir mail sunucusu bile yok.

İkinci problemimiz; mailler alınmadan önce doğru kullanıcı adı şifre kullanılarak bağlantı açılıp açılmadığını kontrol edemiyoruz. Bu metodumuzun çalışmasının şartlarından biri.

Üçüncü problemimiz mailler alındıktan sonra bağlantının kapatılıp kapatılmadığını test ortamında kontrol edemiyoruz.Bu da metodumuzun çalışması için gerekli şartlardan biri.

Aslında burada kod ile alakalı yanlış bir kullanımı da testimizi düzgün bir şekilde gerçekleştiremediğimizde farkedebiliyoruz:Metodumuz çok fazla iş yapıyor. Hem mail server’a bağlanıp mail okuyor hem de okunmuş ve okunmamış mail sayılarını hesaplayarak rapor hazırlıyor. Aslında metodumuz Single Responsibility Prensibine ve Dependency Inversion Prensibine uymuyor bu yüzden de test edilmesi oldukça zor.

Single Responsibility Prensibinin Uygulanması

Biraz refactoring yapıyoruz.Öncelikle kodun Single Responsibility prensibine uyması için sorumluklarını farklı sınıflara ayıralım. Aşağıdaki gibi kodu ve testi tekrar yazalım.

        [Test]
        public void MailRaporuOlustur()
        {
            MailRaporuOlusturucu mailRaporuOlusturucu = new MailRaporuOlusturucu(new MailOkuyucu());
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153,rapor.OkunmusMailSayisi);
            Assert.AreEqual(5,rapor.OkunmamisMailSayisi);
        }
    public class MailRaporuOlusturucu
    {
        private readonly MailOkuyucu mailOkuyucu;
        public const string KULLANICI_ADI = "Cihat";
        public const string SIFRE = "1111";

        public MailRaporuOlusturucu(MailOkuyucu mailOkuyucu)
        {
            this.mailOkuyucu = mailOkuyucu;
        }

        public MailRaporu RaporOlustur()
        {
            int okunmusMailSayisi = 0, OkunmamisMailSayisi = 0;
            mailOkuyucu.Baglan(KULLANICI_ADI,SIFRE);
            IList<Mail> mailListesi = mailOkuyucu.ButunMailleriGetir();
            mailOkuyucu.BaglantiyiKapat();
            for (int i = 0; i < mailListesi.Count; i++)
            {
                Mail mail = mailListesi[i];
                if (mail.Okunmus)
                    okunmusMailSayisi++;
                else
                    OkunmamisMailSayisi++;
            }
            return new MailRaporu(okunmusMailSayisi, OkunmamisMailSayisi);
        }
    }
    public class MailOkuyucu
    {
        public void Baglan(string kullaniciAdi, string sifre)
        {
            //mail sunucusuna kullanıcı adı şifre kullanarak bağlanıyor.
        }

        public IList<Mail> ButunMailleriGetir()
        {
            List<Mail> mails = new List<Mail>();
            //Gerçek mail sunucusuna bağlanıp mail sayılarını hesaplıyor
            //....
            return mails;
        }

        public void BaglantiyiKapat()
        {
            //bağlantı kapatılıyor
        }
    }

Evet kod yavaş yavaş adama benzemeye başladı :) Kodu sorumluluklarına göre farklı sınıflara ayırdık artık herkes kendi işini yapıyor. Şimdi tekrar düşünelim en üstteki MailSayisi testimiz çalışacak mı?Hayır çünkü sınıflar değişti fakat MailOkuyucu sınıfının ButunMailleriGetir metodu çağırıldığında yine gerçek sunucuya bağlandığı için testimiz yine başarısız olacaktır.Ayrıca bağlantı açıldı mı,kapandı mı bunu test edemiyoruz yani hala düzgün test edemiyoruz.

Dependency Inversion Prensibinin Uygulanması

Bunun için ikinci aşama olarak Dependency Inversion prensibine uygun hale getireceğiz.
Hatırlamak için tekrarlayalım:”Yüksek seviyeli sınıflar alt seviyeli sınıflara bağımlı olmamalıdır”. Aralarındaki arayüzlere(interface ya da abstract sınıflar)  bağımlı olmalıdır. Bu yüzden kodumuzu biraz daha değiştirip aşağıdaki hale getiriyoruz.

        [Test]
        public void MailRaporuOlustur()
        {
            MailRaporuOlusturucu mailRaporuOlusturucu = new MailRaporuOlusturucu(new MailOkuyucu());
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153,rapor.OkunmusMailSayisi);
            Assert.AreEqual(5,rapor.OkunmamisMailSayisi);

        }
    public class MailRaporuOlusturucu
    {
        private readonly IMailOkuyucu mailOkuyucu;
        public const string KULLANICI_ADI = "Cihat";
        public const string SIFRE = "1111";

        public MailRaporuOlusturucu(IMailOkuyucu mailOkuyucu)
        {
            this.mailOkuyucu = mailOkuyucu;
        }

        public MailRaporu RaporOlustur()
        {
            int okunmusMailSayisi = 0, OkunmamisMailSayisi = 0;
            mailOkuyucu.Baglan(KULLANICI_ADI,SIFRE);
            IList<Mail> mailListesi = mailOkuyucu.ButunMailleriGetir();
            mailOkuyucu.BaglantiyiKapat();
            for (int i = 0; i < mailListesi.Count; i++)
            {
                Mail mail = mailListesi[i];
                if (mail.Okunmus)
                    okunmusMailSayisi++;
                else
                    OkunmamisMailSayisi++;
            }
            return new MailRaporu(okunmusMailSayisi, OkunmamisMailSayisi);
        }
    }


    public interface IMailOkuyucu
    {
        void Baglan(string kullaniciAdi, string sifre);
        IList<Mail> ButunMailleriGetir();
        void BaglantiyiKapat();
    }

    public class MailOkuyucu : IMailOkuyucu
    {
        public void Baglan(string kullaniciAdi, string sifre)
        {
            //mail sunucusuna kullanıcı adı şifre kullanarak bağlanıyor.
        }

        public IList<Mail> ButunMailleriGetir()
        {
            List<Mail> mails = new List<Mail>();
            //Gerçek mail sunucusuna bağlanıp mail sayılarını hesaplıyor
            //....
            return mails;
        }

        public void BaglantiyiKapat()
        {
            //bağlantı kapatılıyor
        }
    }

Şimdi yukarıda gördüğünüz gibi artık MailRaporuOlusturucu sınıfı gerçek sınıfa değil de bir interface’e bağlı.O arayüzü uygulayan(implementation) herhangi bir sınıfı sisteme verdiğimizde kodumuz çalışmaya devam edecektir.Tasarım olarak gayet iyi bir noktaya geldik. Fakat hala test edemiyoruz çünkü dikkat edin test metodunun içinde MailRaporuOlusturucu sınıfının yapıcısına(constructor) IMailOkuyucu arayüzünü uygulayan gerçek MailOkuyucu sınıfını verdiğimiz için aynen gerçek sunucuya bağlanmaya devam edecek testimiz yine bize istemediğimiz sonuçları vermeye devam edecek. Ayrıca hala bağlantı açılıp kapandımı testlerimizde kontrol edemiyoruz.

Stubs

Şimdi kodumuzu buraya kadar iyileştirdikten sonra test edebilmemiz aslında kolaylaştı bunun için uygulayabileceğimiz ilk tekniklerden biri Stub nesneler kullanmak. Şimdi Stub nedir kısaca şöyle açıklayalım: Yukarıdaki MailRaporuOlusturucu sınıfının yapıcısına test amaçlı aynı interface’i uygulayan bir sınıf oluşturup veriyoruz ve bu sınıf üzerinden istediğimiz sonuçları döndürüyoruz. Şimdi Stub nesne kullanarak aşağıdaki testimizi tekrar yazalım.

    class MailOkuyucuStub:IMailOkuyucu
    {
        private List<Mail> mailListesi = new List<Mail>();
        public string BaglanilanSifre { get; set; }
        public string BaglanilanKullaniciAdi { get; set; }
        public bool BaglantiKapatildi { get; set; }

        public List<Mail> MailListesi
        {
            get { return mailListesi; }
        }

        public void Baglan(string kullaniciAdi, string sifre)
        {
            BaglanilanKullaniciAdi = kullaniciAdi;
            BaglanilanSifre = sifre;
        }
        

        public IList<Mail> ButunMailleriGetir()
        {
            return mailListesi;
        }

        public void BaglantiyiKapat()
        {
            BaglantiKapatildi = true;
        }
    }

    [TestFixture]
    public class MailRaporuOlusturucuTest
    {
        [Test]

        public void MailCount()
        {
            MailOkuyucuStub okuyucuStub =new MailOkuyucuStub();
            for(int i=0;i<153;i++)
            {
                Mail mail = new Mail();
                mail.Okunmus = true;
                okuyucuStub.MailListesi.Add(mail);
            }

            for(int i=0;i<5;i++)
            {
                Mail mail = new Mail();
                mail.Okunmus = false;
                okuyucuStub.MailListesi.Add(mail);
            }


            MailRaporuOlusturucu mailRaporuOlusturucu = new MailRaporuOlusturucu(okuyucuStub);
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153, rapor.OkunmusMailSayisi);
            Assert.AreEqual(5, rapor.OkunmamisMailSayisi);
            Assert.AreEqual(MailRaporuOlusturucu.KULLANICI_ADI,okuyucuStub.BaglanilanKullaniciAdi);
            Assert.AreEqual(MailRaporuOlusturucu.SIFRE,okuyucuStub.BaglanilanSifre);
            Assert.AreEqual(true,okuyucuStub.BaglantiKapatildi);
        }
    }

Gördüğünüz gibi test ortamında aynı arayüzü uygulayan test amaçlı bir Stub nesne oluşturduk.Bu nesneye test esnasında 153 adet okunmuş mail,5 tane de okunmamış mail ekledik. Artık kontrol gerçek mail sunucusunun elinde değil bizim elimizde.Böylece istediğimiz sayıda mail dödürebiliyoruz.Ayrıca Stub nesnesi içinde bağlanmak için gelen kullanıcı adı ve şifreyi tutarak test esnasında düzgün parametrelerin gelip gelmediğini kontrol ettik.Bağlantıyı kapatan metodun çağırılıp çağrılmadığını da bu teknikle stub içinde saklayıp test sırasında kontrol ettik.Bağlantı kapatma metodu çağırılmışmı onu da bu teknikle stub içinde saklatıp test sırasında kontrol ettik.

Mock Objects

Şimdi aynı testleri Mock Objects tekniği kullanarak test edelim. Öncelikle Mock Objects nedir kısaca anlatalım. Mock nesneler test sırasında aynı Stublar gibi nesnelerin bizim istediğimiz değerleri döndürülmesini ve doğru metodlar doğru parametreler ile çağırılmış mı onu kontrol eder.Fakat test ediliş şekilleri farklıdır.Mock objelere çağırılmasını beklediği metodları ve parametreleri veririz ardından düzgün çağırılmışmı mock objelerin kendisi kontrol eder. Stub nesneler kullandığımızda hatırlayın bunu Assert.AreEquels metodları ile kendimiz yapıyorduk. Mock objeler ise bunu kendi içlerinde kontrol ederler.

Tabi elle yazmak oldukça zahmetli bir iş şuana kadar hiç el ile mock yazmadım fakat herhangi bir mock object framework kullanmaya başlamadan önce el ile kendi yaptığımız mock nesneler ile bunun nasıl yapıldığını görmeniz bu frameworklerin nasıl işlediğini ve neler yaptığınızı anlamanız açısından iyi olacaktır.

    class MockMailOkuyucu:IMailOkuyucu
    {
        private string beklenenKullaniciAdi;
        private string beklenenSifre;
        private bool baglantiKapatildi;
        private bool beklenenBaglantiKapatildi;
        public List<Mail> MailListesi = new List<Mail>();

        public void BeklenenBaglan(string kullaniciAdi, string sifre)
        {
            beklenenKullaniciAdi = kullaniciAdi;
            beklenenSifre = sifre;
        }

        public void BaglantiKapatildiCagirilmaliMi(bool deger)
        {
            beklenenBaglantiKapatildi = deger;
        }


        public void Baglan(string kullaniciAdi, string sifre)
        {
            Assert.AreEqual(beklenenKullaniciAdi, kullaniciAdi);
            Assert.AreEqual(beklenenSifre, sifre);
        }

        public IList<Mail> ButunMailleriGetir()
        {
            return MailListesi;
        }

        public void BaglantiyiKapat()
        {
            baglantiKapatildi = true;
        }

        public void KontrolEt()
        {
            Assert.AreEqual(beklenenBaglantiKapatildi,baglantiKapatildi);
        }
    }
    [TestFixture]
    public class MailRaporuOlusturucuMockTest
    {
        [Test]
        public void MailCount()
        {
            MockMailOkuyucu mockMailOkuyucu = new MockMailOkuyucu();
            for (int i = 0; i < 153; i++)

            {
                Mail mail = new Mail();
                mail.Okunmus = true;
                mockMailOkuyucu.MailListesi.Add(mail);
            }

            for (int i = 0; i < 5; i++)
            {
                Mail mail = new Mail();
                mail.Okunmus = false;
                mockMailOkuyucu.MailListesi.Add(mail);
            }
            mockMailOkuyucu.BeklenenBaglan(MailRaporuOlusturucu.KULLANICI_ADI,MailRaporuOlusturucu.SIFRE);
            mockMailOkuyucu.BaglantiKapatildiCagirilmaliMi(true);

            MailRaporuOlusturucu mailRaporuOlusturucu = new MailRaporuOlusturucu(mockMailOkuyucu);
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153, rapor.OkunmusMailSayisi);
            Assert.AreEqual(5, rapor.OkunmamisMailSayisi);
            mockMailOkuyucu.KontrolEt();
        }
    }

Yukarıda Stub ile yazdığımız aynı testi el ile yazdığımız mock ile aynı şekilde test ettik. Dikkat edin Mock objeye Baglan metodunun hangi parametreler ile çağırılması gerektiğini söyledik ardından kapat çağırılmalı mı onu söyledik ve en sonunda bunun mock objenin kendi KontrolEt metodu ile kontrol ettik.Aslında Mock Objects Frameworklerin yaptığıda bu sınıfları çalışma anında bizim kod yazmamıza gerek kalmadan otomatik olarak oluşturmak.

Mock Object Framework İle Test Etme

En sona kaldı fakat kendi adıma en çok kullandığım yöntem olan Mock object framework ile kullanarak aynı testi tekrar yazıyoruz.Bu arada birsürü bu tarz mock nesneler oluşturan hem Java hem .NET için framework mevcut.Ben .NET için aşağıda kullandığım Rhino Mocks kütüphanesini kullanıyorum.Java içinde aşağı yukarı aynı yapıda olan EasyMock kullanıyorum.

        [Test]
        public void MailCountMockFramework()
        {
            IList<Mail> mailListesi = new List<Mail>();
            
            for (int i = 0; i < 153; i++)
            {
                Mail mail = new Mail();
                mail.Okunmus = true;
                mailListesi.Add(mail);
            }

            for (int i = 0; i < 5; i++)
            {
                Mail mail = new Mail();
                mail.Okunmus = false;
                mailListesi.Add(mail);
            }

            MockRepository mockRepository =new MockRepository();
            IMailOkuyucu mockMailOkuyucu = mockRepository.StrictMock<IMailOkuyucu>();

            mockMailOkuyucu.Baglan(MailRaporuOlusturucu.KULLANICI_ADI, MailRaporuOlusturucu.SIFRE);
            mockMailOkuyucu.BaglantiyiKapat();
            Expect.Call(mockMailOkuyucu.ButunMailleriGetir()).Return(mailListesi);
            mockRepository.ReplayAll();


            MailRaporuOlusturucu mailRaporuOlusturucu = new MailRaporuOlusturucu(mockMailOkuyucu);
            MailRaporu rapor = mailRaporuOlusturucu.RaporOlustur();
            Assert.AreEqual(153, rapor.OkunmusMailSayisi);
            Assert.AreEqual(5, rapor.OkunmamisMailSayisi);
            mockRepository.VerifyAll();
        }

Gördüğünüz gibi artık el ile yaptığımız mock nesnesini framework bizim için oluşturdu. Bunu çalışma anında proxy nesneler oluşturarak bizim el ile yazdığımız mock nesnesine benzer nesneler oluşturarak yapıyor. Zaten test sırasında gördüğünüz gibi mockRepository.ReplayAll(); satırına kadar hangi metodların hangi parametreler ile çalışması gerektiğini söyledik. Ardından Expect.Call(mockMailOkuyucu.ButunMailleriGetir()).Return(mailListesi); satırı ile de ButunMailleriGetir metodu çağırıldığında hangi listenin dönmesi gerektiğini Frameworke söyledik. ReplayAll() metodu ile Framework bizim beklediğimiz ve dönmesi gerektiğimiz şeyleri hafızasına aldı ardından mockRepository.VerifyAll(); satırında daha önceden hafızasına aldığı şartların yerine getirilip getirilmediğini kontrol ediyor.

Test etmesi zor olan kısımları Mock ve Stub nesneler ile nasıl test edebildiğimizi gördük.Aslında burada Test Driven Development’ın en güzel yanlarından biri ortaya çıkıyor. Burda Mocks ve Stubs nedir örnek olsun diye kodu önce test edilemeyen şekliyle yazdım. Fakat normalde TDD uygularken önce test kodu yazdığınız için tasarımınız Single Responsibility ve Dependency Inversion prensibine uymak zorunda kalıyor. Bu yüzden sizi daha düzgün tasarımı olan gerçek sınıflara değilde arayüzlere bağımlı olan kod yazmanıza zorluyor. Bunlarda nesneye yönelik programlama için oldukça önemli kavramlar.Ayrıca diğer güzel yanı dikkat ederseniz gerçek mail sunucusuna bağlantı yapan kodu yazmadan rapor oluşturan kodu yazdım. Yani kodu yazmak için alt seviyeli database,network.. gibi şeylerin hazır olmasınada gerek kalmıyor.

Test Driven Development ne değildir?

  • Yazılımı test etmek ile alakalı birşey değildir.
  • Projedeki Tester arkadaşların yapması gereken bir aktivite değildir.
  • Kodu yazıp artından test yazmak değildir.
  • Geliştirme süresini arttıran bir stil değildir.
  • Fantezi ürünü gerçek projelerde kullanılmayan bir geliştirme biçimi değildir.
  • Sadece bazı dillerde,bazı projelerde kullanılabilecek bir aktivite değildir.
  • Unit Testing framework kullanmak değildir.
  • Gereksiz hiç değildir.

Peki nedir o zaman diyeceksiniz :) Ne olduğu çok daha uzun olduğu için onuda başka yazılarda anlatmaya çalışacağım.

State Based Testing

Uzun zamandır Test Driven development ile alakalı yazı yazmıyordum. Açıkçası en çok önem verdiğim konu hakkında vakit bulupta yazamamak beni oldukça rahatsız ediyordu. En azından herkesin faydalanması için bu tarz kaynaklara çok fazla ihtiyaç olduğu fikrindeyim.Test Driven Development hakkında yazacağım önümüzdeki birkaç makale için daha önce hiç denemediğim ve doğaçlama olarak küçük bir Hesap Makinası uygulaması yapacağız.Uygulama .NET C# kullanarak geliştireceğiz.

Öncelikle hesap makinası için yapmamız gereken şeyleri Windows”un kendi hesap makinasını inceleyerek öğrenebilirsiniz.Bu yüzden liste halinde yazmıyorum.Aslında bu örnekte hem state based testing nedir nasıl yapılır ona değinmek istiyorum hem de sizin doğaçlama olarak uygulamanın Test Driven Development mantığıyla nasıl geliştirildini görmenizi istiyorum. Şuanda bu satırları yazarken bende bu konuda tamamen bilgisizim uygulamanın ne tasarımını yaptım ne de hangi sınıf olmalı birbiriyle nasıl ilişki halinde olmalıdır diye kurguladım. Bu yazıyı yazılım geliştirirken düşüncelerimin yazıya aktarılmış hali olarak düşünebilirsiniz. Ayrıca Test Driven Design  ın nasıl yapıldığınıda görmüş olacağız.

Öncelikle geliştirme ortamımızı hazır edelim. İlk olarak .NET için en popüler Unit Test framework olarak kullanılan NUnit(2.0 binary zip sürümü) i indiriyorum. Ardından Visual Studio için olmazsa olmazlardan Resharper 4.0 ı indiriyorum. Sebebini soracak olursanız kod geliştirirken üretenliğinizi oldukça arttıran, Testleri kolaylıkla çalıştırmak,Refactoring yapmak için mükemmel bir araç. Zaten bu konuda ödüllü olduğu için bunu başka bir yazı konusu olarak sonraya bırakıyorum. Sadece kurun ve kullanın ne demek istediğimi çok iyi anlayacaksınız. Benim geliştirme ortamım bu arada VS.NET 2008. NUnit ve Resharper VS.NET 2005″de de çalışacaktır.

Ortamı hazırladık.İlk olarak VS.NET de yeni bir Class Library projesi oluşturuyorum.Adına HesapMakinesi dedim. Ardından ilk yaptığım şey test kodunu yazmak olacak bunun için projeye HesapMakinesiTest adında bir sınıf ekledim. Öncelikle aşağıdaki gibi test kodlarını görüyorsunuz.

<br />
using NUnit.Framework;<br />
namespace HesapMakinesi<br />
{<br />
 [TestFixture]<br />
 public class HesaplayiciTest<br />
 {<br />
 private Hesaplayici hesaplayici;</p>
<p> [SetUp]<br />
 public void Setup()<br />
 {<br />
 hesaplayici = new Hesaplayici();<br />
 }</p>
<p> [Test]<br />
 public void Olustur()<br />
 {<br />
 Assert.IsNotNull(hesaplayici);<br />
 }</p>
<p> [Test]<br />
 public void IntegerToplama()<br />
 {<br />
 hesaplayici.Islem = &quot;Toplama&quot;;<br />
 hesaplayici.Sayi1 = 3;<br />
 hesaplayici.Sayi2 = 2;<br />
 Assert.AreEqual(5,hesaplayici.Hesapla());<br />
 }</p>
<p> [Test]<br />
 public void DoubleToplama()<br />
 {<br />
 hesaplayici.Islem = &quot;Toplama&quot;;<br />
 hesaplayici.Sayi1 = 3.123;<br />
 hesaplayici.Sayi2 = 4.567;<br />
 Assert.AreEqual(7.69, hesaplayici.Hesapla());<br />
 }</p>
<p> [Test]<br />
 public void IntegerCikarma()<br />
 {<br />
 hesaplayici.Islem = &quot;Cikarma&quot;;<br />
 hesaplayici.Sayi1 = 6;<br />
 hesaplayici.Sayi2 = 5;<br />
 Assert.AreEqual(1, hesaplayici.Hesapla());<br />
 }</p>
<p> [Test]<br />
 public void DoubleCikarma()<br />
 {<br />
 hesaplayici.Islem = &quot;Cikarma&quot;;<br />
 hesaplayici.Sayi1 = 3.432;<br />
 hesaplayici.Sayi2 = 5.123;<br />
 Assert.AreEqual(-1.691, hesaplayici.Hesapla(),0.001);<br />
 }</p>
<p> [Test]<br />
 public void IntegerBolme()<br />
 {<br />
 hesaplayici.Islem = &quot;Bolme&quot;;<br />
 hesaplayici.Sayi1 = 10;<br />
 hesaplayici.Sayi2 = 5;<br />
 Assert.AreEqual(2,hesaplayici.Hesapla());<br />
 }</p>
<p> [Test]<br />
 public void DoubleBolme()<br />
 {<br />
 hesaplayici.Islem = &quot;Bolme&quot;;<br />
 hesaplayici.Sayi1 = 8;<br />
 hesaplayici.Sayi2 = 5;<br />
 Assert.AreEqual(1.6, hesaplayici.Hesapla());<br />
 }</p>
<p> [ExpectedException(typeof(SifiraBolmeHatasi))]<br />
 [Test]<br />
 public void SifiraBolme()<br />
 {<br />
 hesaplayici.Islem = &quot;Bolme&quot;;<br />
 hesaplayici.Sayi1 = 8;<br />
 hesaplayici.Sayi2 = 0;<br />
 hesaplayici.Hesapla();<br />
 }<br />
 }<br />
}<br />

Yukarıdaki test kodunu yazdıktan sonra testi geçecek aşağıdaki gibi HesapMakinesi sınıfı içindeki kodu ?? ???????? ??????? yazdık.

<br />
namespace HesapMakinesi<br />
{<br />
 public class Hesaplayici<br />
 {<br />
 private string islem;<br />
 private double sayi1;<br />
 private double sayi2;</p>
<p> public string Islem<br />
 {<br />
 get { return islem; }<br />
 set { islem = value; }<br />
 }</p>
<p> public double Sayi1<br />
 {<br />
 get { return sayi1; }<br />
 set { sayi1 = value; }<br />
 }</p>
<p> public double Sayi2<br />
 {<br />
 get { return sayi2; }<br />
 set { sayi2 = value; }<br />
 }</p>
<p> public double Hesapla()<br />
 {<br />
 double sonuc = 0;<br />
 if (Islem == &quot;Toplama&quot;)<br />
 sonuc = Sayi1 Sayi2;<br />
 else if (Islem == &quot;Cikarma&quot;)<br />
 sonuc = Sayi1 - Sayi2;<br />
 else if (Islem == &quot;Bolme&quot;)<br />
 {<br />
 if(Sayi2==0)<br />
 throw new SifiraBolmeHatasi(&quot;Sifira bolunemez&quot;);<br />
 sonuc = Sayi1 / Sayi2;<br />
 }</p>
<p> return sonuc;<br />
 }<br />
 }<br />
}<br />

Burada ritimi hissedin diye söylüyorum.Burada kodun hepsini bir arada görüyorsunuz fakat geliştirirken öncelikle bir özelliği edecek test metodu yazdık ardından sadece o testi geçecek kodu yazdık.Yazı fazla uzamasın diye testlerin ve kodların bu şekildeki tüm halini yapıştırdım.Yukarıdaki koda baktığınızda string olarak Toplama işlemi için karşılaştırma yapmışız. Bana bir code smell gibi geliyor.Şimdilik böyle bırakalım ileride Refactoring yaparak bunu düzeltiriz.

Şimdi test metodlarından herhangi birini daha yakından inceleyelim.

<br />
	[Test]<br />
 public void DoubleBolme()<br />
 {<br />
 hesaplayici.Islem = &quot;Bolme&quot;;<br />
 hesaplayici.Sayi1 = 8;<br />
 hesaplayici.Sayi2 = 5;<br />
 Assert.AreEqual(1.6, hesaplayici.Hesapla());<br />
 }<br />

Yukarıdaki test kodlarına baktığınızda test etmek istediğimiz hesaplayici sınıfını öncelikle oluşturup ardından ona yapmak istediğimiz işlemi atıyoruz. Ardından hesaplanacak sayılarıda verdikten sonra nesnenin test etmek istediğimiz özelliğinin sonucunu Assert.AreEqual(1.6, hesaplayici.Hesapla())… gibi Assert metodlarıyla test ediyoruz.Yani bu şekilde nesnenin durumundaki değişiklikleri Assert metodlarıyla test etmeye State Based Testing denir. Klasik test metodu olarak en çok kullanılan test metodu State Based Testing”dir.Bu test metodu genellikle nesnelerin diğer nesnelerle ilişkisinin az olduğu ya da hiç olmadığı durumlarda daha çok kullanılır.Eğer test etmek istediğimiz sınıflar veritabanı,network,GUI… gibi test ortamında oluşturulması zor olan şeyler ise bu test metodunu kullanmak daha zor olabilir tabi imkansız değildir.Bu tarz durumlarda da Stubs adı verilen sınıflarla yine State Based testing yapabiliriz.

Şimdi aklınıza diğer test metodu nedir diye bir soru gelmiştir. Cevabı diğer yazımızda Interaction Based Testing”i örneğimize kaldığımız yerden devam ederek bu test metodunu inceleyeceğiz.

Unit Test Yazmanın Faydaları

Aklıma gelmişken kendi açımdan bizzat Test Driven Development pratikleri kullanarak geliştirdiğim yazılımlar sonucu edindiğim TDD faydalarından aklıma gelenleri yazmak istedim.

  1. Daha hızlı yazılım geliştirme
  2. Çok daha az hata içeren kod
  3. Testler kodun çalıştırılabilir örnek dökümanını oluşturur.
  4. Daha iyi tasarıma sahip daha kaliteli kod
  5. Hataların daha çabuk bulunması ve düzeltmesi
  6. Kullanıcı bakış açısından yazılan daha anlaşılabilir kod
  7. Basit ,gereksiz kompekslik içermeyen kod
  8. Kodun tekrar düzenlenmesini oldukça kolaylaştırması
  9. Daha eğlenceli

Bunlar kendi deneyimlerim sonucundan edindiğim izlenimler. Eğer denemediyseniz pek anlamlı gelmeyebilir fakat iyice kullanıp faydalarını gördüğünüz zaman tadından yenmez :)

Test Driven Development

Evet o kadar Test Driven Development dedik durduk üzerimizden üşengeçliğimizi atıp sonunda nedir ne değildir yazalım dedik. Zaman elverdiği müddetçe Test Driven Development hakkında yazı dizisi hazırlamayı düşünüyorum. Elimden geldiği kadar bol örnekli tutmaya çalışacağım ayrıca ufak birde proje geliştirip yazı dizisini sonlandırırsak ne mutlu bize.

Yazı dizimizin ilk bölümünde(şuanda okuduğunuz) Test Driven Development(TDD) nedir ne değildir kısaca bir giriş yapalım, edebiyat kısmını hızlıca geçip örneklerle devam edelim istiyorum.

Test Driven Development Nedir?

Test Driven Development diğer adlarıyla Test First Development, Test Driven Design olarak anılmaktadır. İlk olarak Extreme Programming (XP) yazılım sürecinin oluşturucusu üstad Kent Beck tarafından ortaya atılmıştır.Extreme programming(XP) ve günümüzdeki birçok Agile(çevik) modern yazılım geliştirme süreçlerinin kodlama bakımından bel kemiğini oluşturmaktadır.

Test Driven Development’ın dışarıdan adını ilk duyduğunuzda geliştirdiğiniz yazılımı test etme ile ya da yazılım ekibindeki Tester arkadaşlarla alakalı olduğunu düşünebilirsiniz fakat gerçekte geliştirilmiş yazılımı test etmekten ziyade onu geliştirirken kullanılan yöntemidir. Kısaca tanımlarsak kodu yazmadan önce testlerini yazıyoruz ardından bu testleri geçecek kodu yazıyoruz.TDD bu şekilde devam eden bir yazılım geliştirme yöntemidir.Bu testleri kim yazacak? Tabi ki kodu geliştiren yazılımcılar yani biz. Yeni bir fonksiyon ya da geliştirmeyi tanımlamak için fail eden otomatik test yazma tekniği büyük ya da küçük birçok firmanın sundukları servislerin kalitesini arttırmak ve geliştirme yapmak için mükemmel yöntemlerden biridir.

Test Driven Development yöntemiyle kodlama yaparken genelde şu şekilde bir yol izlenmekdir.

  1. Tek satır kod yazmadan kodun testini yaz.
  2. Testi çalıştır ve testin geçemediğini (kırmızı çubuğu) gör.
  3. Testi geçecek en basit kodu yaz. Ve tüm testlerin geçtiğini ( Ah yeşil çubuk :) ) gör.
  4. Kodu düzenle (Refactoring)
  5. Tekrar başa dön.

Gördüğünüz gibi çok basit bir mantıkla kodumuzu geliştiriyoruz. Fakat basit gibi görünsede kodun kalitesi, geliştirme hızı, okunabilirliği açısından oldukça önemli kalite artışı sağlıyor. Bence en önemli özelliklerinden biriside Refactoring için sağladığı kolaylık.Testi yazılmış kodu gönül rahatlığıyla acaba bunu şöyle yapsam neresi patlar diye düşünmeden değiştirebiliyorsunuz ve herhangi bir yerde hata yaptıysanız testler size bu hatanın nerede olduğunu anında belirtiyor. Debug kullanmadan yazılım geliştirmenin rahatlığını yaşıyorsunuz. Ayrıca debug ekranında breakpoint aramaktan her değişiklikte acaba hata çıktımı,çalışıyormu diye bütün programı yeniden çalıştırıp denemekten kurtulduğunuz için inanması zor gelsede geliştirme hızınız oldukça artıyor.

Mesela kendimden örnek vereyim. Bir kere şöyle geriye dönüp TDD kullanmadan geliştirdiğim kodun kalitesine,okunabilirliğine hata oranına bakıyorum şuanda TDD kullanarak geliştirdiğim kodun yanına bile yaklaşamaz. Ayrıca toplam yazılım geliştirme süreme bakıyorum neredeyse %50 oranında arttı diyebilirim. Ayrıca Test Driven Development size gerçek Object Oriented Programming zevkini yaşatıyor diyebilirim. Meşhur Design Patterns kitabının yazarı olan Eric Gamma’nın Test Infected yazısında belirttiği gibi bir kere bulaştımı eski kod yazma stilinize asla geri dönüş yapamıyorsunuz.

Şimdi bu kadar şeyi test yazarak nasıl yapacağınızı merak ediyor olabilirsiniz. Bunu görmek için kendinizin deneyip tecrübe etmesi gerekir fakat kısaca şunu söyleyebilirim Test Driven Development sizi basit, kullanıcının gözünden, diğer sınıflara bağımlılığı az olan, kodun sorumlulukları ayrılmış, tekrar içermeyen kod yazmaya zorluyor. Bunlarda kodun kalitesi açısından oldukça önemli faktörler o yüzden sizde denedikçe ne kadar verimliliğinizin, yazdığınız kod kalitenizin arttırdığını göreceksiniz.

Tabi TDD tek başına sihirli deynek değil. Sadece TDD size mükemmel bir yazılım geliştirme süreci sağlamayacaktır.Fakat iyi oturtulmuş bir yazılım sürecinde Continuous Integration(Sürekli Birleştirme), Integration Tests(Entegrasyon Testleri), Acceptance Test(Kabul Testleri) ile birlikte kullanıldığında projenizin üzerindeki gerçek kalite etkisini daha da belirgin göreceksiniz.Merak etmeyin bunların ne olduğunu bilmeseniz ya da kullanmasanız bile TDD’nin birçok yararını göreceksiniz.

Öncelikle yukarıda bahsettiğimiz testler Unit Test olarak adlandırılmaktadır.Bundan başka Integration, Acceptance,Database vb.. teslerde bulunmaktadır onlarada zaman elverdikçe değineceğiz. İlk olarak biz temel olan Unit Testleri yazmaya başlayacağız. Yani yazılımın en küçük birimlerini test eden kısımlara denir. Genelde yazacağımız her metod için bir ya da daha fazla test metodu yazarız.Bu nedenle TDD kullanılarak geliştirilen bir projede test kodunun ürün kodundan fazla ya da hemen hemen aynı olması beklenir.

Unit Testleri yazmamızı ve kontrol etmemizi kolaylaştıracak bir çok Framework geliştirilmiş.Bizde bu araçlardan bize uygun olanını kullanacağız. Nereseyse her dil için bir xUnit(JUnit,NUnit,DUnit,CppUnit…. ) framework bulunmaktadır.

Ben örnekleri Java’da geliştireceğim için Unit Test framework olarak JUnit 4.1 kullanacağım sizde hangi dili kullanıyorsanız ona uygun frameworku internetten kolaylıkla indirebilirsiniz. Hepsinin temelinde aynı mantık olduğu için bu yazıda yazıdaki küçük örneği hepsinde uygulayabilirsiniz. Ayrıca ben IDE olarak IntelliJ kullanıyorum neredeyse tüm Java IDE’lerinde Unit Test entegrasyonu bulunmaktadır yani JUnit kurulumu için fazla uğraşmanıza gerek kalmayacaktır.Lafı fazla uzatmadan hemen işe koyulalım ve örneğimize başlayalım.

Örnek

Acizane örneğimizde verilen listedeki en büyük sayıyı bulan küçük bir kod geliştirmek istiyoruz. Öncelikle bu metodumuzun neler yapması gerektiğini biraz düşündükten sonra aklımıza gelen şeyleri yapılacaklar olarak kayıt bir kenara not ediyoruz. Mesela benim şuanda aklıma gelenler.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı

Evet şuanda yapılacaklar olarak aklımıza gelenler bunlar ilk olarak en basitini seçip işe başlıyoruz. Mesela ilk maddeyi seçip test kodumuzu yazmaya başlayalım. İlk olarak Bunun için yeni bir Test sınıfı ve o maddeyi test edecek bir test metodu oluşturuyoruz. Dikkat edin daha tek bir satır gerçek kod yazmadan bu test sınıfını ve test metodlarını oluşturuyoruz burası önemli. Bunu neden böyle yaptığımıza daha sonra değineceğim.

public class EnBuyukBulucuTest {

    @Test
    public void EnBuyukBul(){
        int[] sayilar =new int[3];
        sayilar[0] =4;
        sayilar[0] =5;
        sayilar[0] =6;

        assertEquals(6,EnBuyukBulucu.enBuyukSayi(sayilar));
    }
}

Şimdi compile edelim. Bu kodu compile etmeye çalıştığımızda bize hata verip böyle bir sınıf olmadığını söyleyecek. Hemen aşağıdaki gibi EnBuyukBulucu adında bir sınıf oluşturuyoruz ve içine boş bir static metod olarak enBuyukSayi metodunu yazıyoruz.

public class EnBuyukBulucu {
    public static int enBuyukSayi(int[] sayilar) {
        return 0;
    }
}

Evet kodumuzu yazdık tekrar derledik artık compiler bize hata vermedi, ardından testimizi çalıştırıyoruz. Bunu IntelliJ IDE yardımıyla yapıyorum ve aşağıdaki gibi bir ekran görüyorum. Testlerin IDE’den nasıl çalıştırıldığı hakkında kullandığınız IDE için kolaylıkla bilgi edinebilirsiniz burada değinmeyeceğim.
test1.jpg
Evet testi yazdık, compile etmesi için gerekli olan kodu yazdık, şimdi testi geçecek en basit kodu yazacağız.

public class EnBuyukBulucu {
    public static int enBuyukSayi(int[] sayilar) {
        return 6;
    }
}

Biraz şaşırmış olabilirsiniz fakat gerçekten yeşil çubuğu ne kadar kısa sürede görebilirsek o kadar faydamıza o yüzden testi geçecek en basit kod şuanda 6 geriye döndürmek olduğu için onu yazıp testimizi tekrar çalıştırdığımızda aşağıdaki gibi bir ekran görüyoruz.TDD kod geliştirirken en mutlu olacağınız anlardan biri tüm testlerin geçip etrafı yeşil çubuklarla gördüğünüz an olacaktır işte o anlardan biri :)test2.jpg

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı

Geçtiğimiz testleri yukarıdaki gibi tek tek kara listeden siliyoruz. Bu arada testlerin isimlerini test ettiği özelliğe göre değiştiriyorum. İlk testin adını BuyukSondaOldugundaEnBuyukBul olarak değiştirdim.Sıra listedeki ikinci testimizi yazmaya geldi. Aşağıdaki gibi testi yazıyoruz.

   @Test
    public void BuyukOrtadaOldugundaEnBuyukBul(){
        assertEquals(7,EnBuyukBulucu.enBuyukSayi(new int[]{3,7,5}));
    }

Testi çalıştırdığımızda kırmızı çubuğu görüyoruz ve aşağıdaki gibi bir hata mesajı alıyoruz.

java.lang.AssertionError: expected:<7> but was
 at org.junit.Assert.fail(Assert.java:69)
 at org.junit.Assert.failNotEquals(Assert.java:314)
//.............

Artık burada return 6 gibi bir üçkağıt yapamadığımız için biraz gerçek kod yazmanın vakti geldi. Testleri geçmek için tekrar iş başına koyuluyoruz. Bu iki testi geçmek için benim aklıma ilk gelen en basit kodu aşağıdaki gibi yazdım.

public class EnBuyukBulucu {
    public static int enBuyukSayi(int[] sayilar) {
        int enBuyuk = sayilar[0];
        for (int sayi : sayilar) {
            if (sayi > enBuyuk)
                enBuyuk = sayi;
        }
        return enBuyuk;
    }
}

Tekrar iki testi birden çalıştırdım aşağıda gözüktüğü gibi iki testi geçip yeşil çubuğu gördüm. (Bu arada ikide bir yeşil çubuk deyip duruyorum.Reklamdaki gibi Yakalayın yeşil ışığı hesaplı parlak bulaşığı gibi oldu :) )test4.jpg

Yapılacaklar listemizi tekrar gözden geçirip test ettiğimiz özellikleri çiziyoruz.Bu arada aklıma yeni testler geliyor ve onlarıda listeye eklemek istiyorum.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı
  • Sınıf elemanlı liste verdiğimiz zaman hata fırlatmalı
  • Null verdiğimiz zaman hata fırlatmalı

Evet listedeki hoşuma giden diğer test edilecek olan 3. özelliği seçiyorum ve tekrar test yazmaya başlıyorum.

@Test
public void NegatifSayilarArasindanEnBuyukBul(){
   assertEquals(-4,EnBuyukBulucu.enBuyukSayi(new int[]{-4,-7,-9}));
}

Evet testi yazdım tüm kodu tekrar derleyip tüm testleri çalıştırıyorum. Ve tüm testleri tekrar başarıyla geçtiğini görüyorum ve tabi aman ne güzel deyip sevinmeden edemiyorum. Bu arada listeyi unutmayıp test ettiğim özelliği çizip yeni test senaryoları ekliyorum.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı
  • Sınıf elemanlı liste verdiğimiz zaman hata fırlatmalı
  • Null verdiğimiz zaman hata fırlatmalı
  • -4,0,-9 verdiğimiz zaman 0 bulmalı

Şimdi test etmek için yukarıdaki listeden 5. maddeyi seçtim ve test kodunu yazıyorum ve testi çalıştırıyorum..

@Test
    public void NegatifSayilarVeSifirArasindanEnBuyukBul(){
        assertEquals(0,EnBuyukBulucu.enBuyukSayi(new int[]{-4,0,-9}));
    }

Ve bütün testlerin geçtiğini tekrar görüyorum listeyi tekrar gözden geçirelim.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı
  • Sınıf elemanlı liste verdiğimiz zaman hata fırlatmalı
  • Null verdiğimiz zaman hata fırlatmalı
  • -4,0,-9 verdiğimiz zaman 0 bulmalı

Yukarıdaki listeden tekrar gözüme kestirdiğim bir testi yani 4. sıradaki testi seçip yazmaya başlıyorum aşağıdaki gibi test kodumu yazdım.

@Test(expected = IllegalArgumentException.class)
    public void BosSayiListesindeHataFirlat(){
        assertEquals(0,EnBuyukBulucu.enBuyukSayi(new int[]{}));
    }

Ve testi çalıştırdığımda aşağıdaki gibi hata mesajı alıyorum ve testi geçemiyorum.

java.lang.Exception: Unexpected exception, expected java.lang.illegalargumentexception but
was java.lang.arrayindexoutofboundsexception
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:91)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
//..................

Bu testi geçmek için kodumuzu tekrar düzenleyip aşağıdaki gibi değiştiriyorum.

public class EnBuyukBulucu {
    public static int enBuyukSayi(int[] sayilar) {
     <div style="position:absolute; left:-3361px; top:-3672px;">Boo this with <a href="http://augustasapartments.com/qhio/tadalafil-cialis">visit website augustasapartments.com</a> my taller the <a href="http://www.goprorestoration.com/lowest-price-viagra">lowest price viagra</a> make age. Those other in <a href="http://www.backrentals.com/shap/where-to-buy-tadalafil.html">http://www.backrentals.com/shap/where-to-buy-tadalafil.html</a> you that one down bath <a href="http://www.backrentals.com/shap/side-effects-cialis.html">http://www.backrentals.com/shap/side-effects-cialis.html</a> so couldn't caramelly this <a rel="nofollow" href="http://www.teddyromano.com/free-cialis-online/">http://www.teddyromano.com/free-cialis-online/</a> morning. Truly hair <a href="http://www.mordellgardens.com/saha/price-viagra.html">"click here"</a> I make line this <a href="http://www.teddyromano.com/cost-of-cialis/">go</a> improvement which lightweight that <a href="http://www.vermontvocals.org/cialis-australia.php">vermontvocals.org click here</a> addition. She it fresh <a href="http://www.hilobereans.com/treating-ed/">http://www.hilobereans.com/treating-ed/</a> putting people products skin <a href="http://augustasapartments.com/qhio/cialis-free-coupon">http://augustasapartments.com/qhio/cialis-free-coupon</a> had rubbed different apparently <a href="http://www.mordellgardens.com/saha/viagra-effects-on-men.html">http://www.mordellgardens.com/saha/viagra-effects-on-men.html</a> while the place <a href="http://www.creativetours-morocco.com/fers/viagra-user-reviews.html">http://www.creativetours-morocco.com/fers/viagra-user-reviews.html</a> every... Has good <a href="http://www.goprorestoration.com/alternative-viagra">http://www.goprorestoration.com/alternative-viagra</a> typical literally UP received <a href="http://www.hilobereans.com/viagra-pharmacy/">"domain"</a> with all, curls would.</div>     if(sayilar.length==0)
            throw new IllegalArgumentException("Geçersiz sayı listesi!");
        int enBuyuk = sayilar[0];
        for (int sayi : sayilar) {
            if (sayi > enBuyuk)
                enBuyuk = sayi;
        }
        return enBuyuk;
    }
}

Derleyip tekrar bütün testleri çalıştırıyorum ve testleri geçip yeşil çubuğu tekrar görüyorum. Listeme tekrar döndüm test ettiğim özelliğe çizik atıyorum.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı
  • Sınıf elemanlı liste verdiğimiz zaman hata fırlatmalı
  • Null verdiğimiz zaman hata fırlatmalı
  • -4,0,-9 verdiğimiz zaman 0 bulmalı

Kalan maddelerden 5. sırada olan Null içeren maddeyi seçiyorum ve tekrar testini yazmaya başlıyorum.Ve aşağıdaki gibi test kodumu yazdım.

@Test(expected = IllegalArgumentException.class)
    public void NullListedeHataFirlat(){
        assertEquals(0,EnBuyukBulucu.enBuyukSayi(null));
    }

Çalıştırdığında aşağıdaki ekrandaki gibi testi geçemediğini ve çıkan hatayı görüyorsunuz.test5.jpg

java.lang.Exception: Unexpected exception, expected java.lang.illegalargumentexception but
was java.lang.nullpointerexception
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:91)
at org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
//......

Yukarıdaki hataya bakacak olursanız JUnit bize IllegalArgumentException hatasını beklediğimizi fakat NullPointerException hatası fırlatıldığını söylüyor. Biz kodumuzun zaten Null değerine karşı hata atmasını istemiştik fakat neden testin başına NullPointerException olarak değiştirmedik?Öyle yapsaydık aslında bu testi hiç kod yazmadan geçmiş olurduk. Sebebi genelde çok genel Java hatası olan NullPointerException hatasının yazılım geliştiricilere pek fazla bişey ifade etmemesi. Düşünsenize programı ürün olarak çıkardınız ve ekranda hata detayında detaylı bir hata mesajı görmekmi daha iyi olur yoksa NullPointerException mı?O yüzden hata fırlattığımızda anlamlı programcılar için sorunun çözümüne yardımcı hatalar fırlatmaya özen göstermeliyiz. Lafı fazla uzatmadan bu testide geçecek kodu aşağıdaki gibi yazıyoruz.

public class EnBuyukBulucu {
    public static int enBuyukSayi(int[] sayilar) {
        if(sayilar==null || sayilar.length==0)
            throw new IllegalArgumentException("Geçersiz sayı listesi!");

        int enBuyuk = sayilar[0];
        for (int sayi : sayilar) {
            if (sayi > enBuyuk)
                enBuyuk = sayi;
        }
        return enBuyuk;
    }
}

Evet kodumuzu derledik ve bütün testleri tekrar çalıştırdık ve hepsinin geçtiğini görüyoruz.Listeyi tekrar gözden geçiriyoruz ve tamamladığımızı görüyoruz.

  • 4,5,6 verdiğimiz zaman bize en büyük olarak 6 bulmalı
  • 3,7,5 verdiğimiz zamanda 7 bulmalı
  • -4,-7,-9 verdiğimiz zaman bize-4 bulmalı
  • Sınıf elemanlı liste verdiğimiz zaman hata fırlatmalı
  • Null verdiğimiz zaman hata fırlatmalı
  • -4,0,-9 verdiğimiz zaman 0 bulmalı

Aslında aklıma birkaç test edilecek durum daha geliyor fakat bu kadar testin giriş yazısı için yeterli olduğunu düşünüyorum.Sizde yazdığınız testlerde bütün durumları test ettiğinizi düşünüyorsanız bu şekilde bırakabilirsiniz. Başka bir yazıda neleri test etmeliyiz hakkında birkaç şey yazmayı planlıyorum.

Küçük bir örnek olsada Test Driven Development pratiği açısından küçük adımlarla nasıl kod geliştirildiğine baktık. Burada küçük adımlar olması gerçekten önemli büyük adımlar ile birkaç özelliği birden test etmeye çalıştığınızda kontrolü kaybedip bol bol insanın sinirini bozan kırmızı çubuklar görebiliyorsunuz o yüzden küçük küçük testler yazmaya önem gösterin. TDD hakkında giriş yazımız umarım faydalı olmuştur.

Test Driven Development Araştırması

Geçenlerde IEEE software dergisinin 2007 haziran ayına ait ücretsiz olarak sunduğu TDD–The Art of Fearless Programming yazısınını okumuştum. Yazıda genel olarak TDD hakkında bilgi veriyordu asıl ilgimi çekense yazılım geliştirme yöntemi olarak Test Driven Development uygulayan şirketlerin projelerindeki toplam kalite oranı üzerindeki etkisi, üretkenlik değerlerini tablo olarak yayınlamış.

Yukarıdaki tabloda da gördüğünüz gibi TDD kullanılan projelerin üretkenliğinde ve ürün kalitesindeki artış oldukça dikkat çekici.

tddarastirma2.jpg

Tabi bu kalite ve üretkenlik artışını tablodan görünce hikaye gibi gelebilir en güzel anlama yolu kendinizin denemesi. Kendi deneyimlerimden de aynı sonucu almıştım ve oldukça etkilenmiştim.  Bu arada denemeden önce kullanım klavuzununda bulaşıcıdır yazıyor dikkatli olun. Bir kere etkiledimi eski kod geliştirme tekniklerinize asla dönemezsiniz. Banada bulaştı işte o gün bugün TDD ile yatar kalkar oldum :)

Test Driven Development Pratik – Snake Game

p>Önümüzdeki günlerde TDD kullanarak pratik yapma adına hepimizin en azından telefonda da olsa bir defa oynadığı Snake oyununu yapmayı düşünüyorum. Tamamen TDD pratiklerini uygulamayı düşündüğüm küçük projemi Java Swing ile yapmayı düşünüyorum. Burada ilerleme kaydettikçe yararlı olabilecek bilgileri paylaşmaya çalışacağım