Ü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.

Hiç yorum yok:
Yorum Gönder