23 Ocak 2011 Pazar

Unit testin önemi ve Mockito

Ülkemizde yazılım evlerinin ve yazılım geliştiricilerinin çok üzerinde durmadığı ama önem vermesi gereken konulardan birisi unit test yazma. Öncelikle bu konudaki araştırma sonuçlarını görelim:




Grafiktende görülebileceği üzere kodlama esnasında bulunan bir bug'ın maliyeti 25 dolar, unit test'lerde bulunan bir bug'ın maliyeti 100 dolar, QA tarafından bulunan bug'ın maliyeti 1000 dolar, ürün release edildikten sonra çıkan bir bug'ın maliyeti ise 16000 dolar. Grafik her bir bug'ın yazılım geliştirme döngüsünde ne kadar erken bulunup çözülmesi gerekliliğini çok iyi ortaya koyuyor.

Ülkemizde test driven development, behavior driven development gibi agile software development metodolojileri pek kullanılmıyor. Genellikle projelere hızla başlanır, doğru düzgün dizayn yapılmaz, son tarihe yetiştirmeye çalışılırken testlere ve dökümanlara önem verilmez. Ancak iş müşterinin projeyi kabul etmesine, veya sonrasında bu projenin desteğinin verilmesine gelince geriye dönülüp test ve döküman açıkları kapatılmaya çalışılır. Özellikle dizayna yeterince önem verilmeyip object-oriented, cohesion vs gibi ideal yapı özelliklerine uzak projelerde unit test yazmak bir hayli zordur. İç içe geçmiş veya tek amaç yerine herşeyi yapmaya çalışan class'lar - eğri büğrü object-oriented yapı, 1000 satırlık metotlar, resource injection veya db pooling yapmaya çalışırken "nasıl test ederim"i düşünmeme vs ile uğraşmak zorunda kalırsınız. Nihayetinde unit test coverage yüzdeniz ideal olandan çok uzak kalır. Bu problemlerin dermanı, kolay kullanımlı, syntax'i kısa, öz ve kolay okunur olan Mockito ile "spying on real objects" yapalım.

(Mockito ile basit mocking örneklerinden kapsamlı javadoc'una kadar herşey için http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html sitesine bakabilirsiniz)

MyClass isminde bir sınıfımız ve bu sınıfın 3 tane metodu olsun. Bu metotlardan bir tanesi veritabanı işlemleri yapıyor olsun. Diğer 2 metotta bu metoda bağımlı olsun.

    package test.package1;

    public class MyClass {
      
        public int method1() {
            System.out.println("Method1 started");
            /*
             * some db operations
             * OR
             * some other class dependencies that might return a              * beautiful NullPointerException when unit testing
             */
            return 1;
        }
      
        public int method2() {
            System.out.println("Method2 started");

            int value = method1();
            /*
             * does something with the return value of method1
             */
            return value;
        }
      
        public int method3() {
            System.out.println("Method3 started");
          
            int value = method2();
            /*
             * does something with the return value of method2
             */
            return value;
        }
    }

Metot3 için unit test yazmaya çalışırsak metot1'in veritabanı bağımlılığından dolayı bu test unit test olmaktan çıkıp functional test olacaktır. Ayrıca veritabanı erişimini bir pooling ile yapıyor olabiliriz ve bu veritabanı havuzu sınıfımız ayakta ve çalışır olmayabilir. Bu durumda unit testimiz metot3'ü çalıştırdığında metot1'de güzel bir NullPointerException yiyebiliriz. Bu durumda mock yada stub sınıfları yazmak yerine Mockito ile basit bir çözüm geliştirelim:

Farzedelim ki sadece metot1'i dışa bağımlılığından dolayı sahtesiyle değiştirmek (mock) istiyoruz. method2 ve tabiki uğruna testler yazıp düzgün çalışıp çalışmadığını test edeceğimiz method3'ün kendi işlerini yapmasına izin vereceğiz. Örnek test kodu şu şekilde olabilir:

package test.package1;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

import static org.mockito.Mockito.*;

public class TestMyClass {

    private MyClass spyInstance;
   
    @Before
    public void setUp() throws Exception {
           spyInstance = spy(new MyClass());
          
        /*
         * when method1 of spyInstance is called,
         * return 0
         */
           doReturn(0).when(spyInstance).method1();
    }
   
    @Test
    public void test1() {
        /*
         * if spying is working then method3 will return 0
         * and test will succeed otherwise
         * method3 will return 1 and test will fail
         */
        assertEquals(0,spyInstance.method3());
    }
}
Bu testin terminal çıktısı şöyle olacak:
method3 started
method2 started

Örneğimizde kullandığımız metotlar herhangi bir argüman almıyordu. Şayet argüman alan metotlar kullansaydık mockito'ya ihtiyacımıza göre:
method1'e 0 değeri verildiğinde;
    doReturn(0).when(spyInstance).method1(0);
yada method1'e herhangi bir integer değeri verildiğinde;
    doReturn(0).when(spyInstance).method1(anyInt());

şeklinde doReturn statement'ları yazmamız gerekirdi.


Sonuç olarak Mockito ile testlerinizi hızlıca ve unit test mantığından sapmadan yazabiliyorsunuz. En azından denenmesini tavsiye ediyorum. Yüksek bir öğrenme ivmesi mevcut. İhtiyacınız oldukça daha derinlerine inip bu framework'ün yeteneklerini keşfedebilirsiniz.

17 Ocak 2011 Pazartesi

Siftah

Merhaba,

Uzun zamandan sonra yeniden blog yazmaya başladım. Temel maksadım bildiklerimi paylaşmak, paylaştıkça sektörümüzün ve ülkemizin daha iyiye gitmesine vesile olmak.

Ülke olarak maalesef bilim ve teknolojiyi geriden takip ediyoruz. Başkaları yapıyor, biz şanslı isek çabuk uyanıp çok geç kalmadan alıyoruz. Örnek vermek gerekirse Google Android mobil işletim sistemini çıkardı, Avrupa ve Amerika'da insanlar 2007-2008'de keşfedip yazılım üretmeye başladılar. (Erken başlangıç yapmış olmanın faydalarını 2009 başında piyasaya sürülen Advanced Task Manager uygulamasının sahibinin blogundan görebilirsiniz. -> http://arronla.com/2010/08/android-revenue-advanced-task-manager )

İnternet sayesinde günümüzde en azından popüler bilgiye ulaşmak çok çok kolay. 90-95 öncesi doğanlar okullarında verilen ödevlerin ansiklopedilerden, kütüphanelerden, kitaplardan araştırıldığı günleri hatırlamakta zorlanmayacaktır. Bir konu için birkaç saat araştırma yapmanız gerekirdi ki bazen buna rağmen aradığınızı bulamayabilirdiniz. Şuanda bu süreyi eğer aradığınızı ve arayacağınız yeri bilirseniz genellikle saniyelerle ölçmek mümkündür.

İnternet yakın geçmişin en önemli icadı olması rağmen birçok açıdan çöplüğe dönmüş durumda. Kopyala & yapıştır'ı hayat felsefesi haline getirmiş bilimum niteliksiz ve özgün içerik üretme özürlü insanın ürettiği bir çöplük.

Kısacası bu blogda yapmak istediğim şey çöplüğe bir çöp daha atmak yerine özellikle içinde bulunduğum, hergün tecrübe ettiğim yazılım ve teknoloji alanlarında özgün içerik üretip kendimin, sizlerin ve sektörün gelişimine katkıda bulunmaktır.

Genel olarak ilgilendiğim ve paylaşımlarda bulunmaya çalışacağım alanlar; yazılım, yazılım geliştirme süreçleri ve araçları, yeni yazılım teknolojileri, yazılım kalitesi, mobil yazılım geliştirme, kişisel gelişim, iş fikirleri, vs.

Özel olarak ise Android, Mockito, Unit Test, Eclipse, Java, Linux, Ubuntu, WebServices vs.

Faydalı olabilmesi dileğiyle...