Unit Testing ve Mockito
Merhaba, bu yazıda genel olarak yazılım testinin ne olduğundan ve Unit Testing’den bahsettikten sonra, Mockito kütüphanesi kullanarak yazdığım örneği göstereceğim.
Öncelikle yazılım testinin ne olduğundan başlayalım.
Aslında yazılım testini açıklayan birçok tanım yazabiliriz. Fakat en genel tanımıyla yazılım testi “Bir yazılımın, kendisinden beklenen özellikleri karşılayıp karşılayamadığını incelemek amacıyla yapılan işlemlerdir. Bu şekilde yazılımdaki hatalar bulunup düzeltilebilir ve gereksinimlere uygun hale getirilebilir.”
Peki ya Yazılım Testinin Amaçları Nelerdir? Neden Gereklidir?
· Gereksinimlerin ne kadar karşılandığından emin olmak,
· Ürünün kalite düzeyine karşı güven oluşturmak,
· Ürünün test işleminin tamamlandığını ve müşterinin beklediği şekilde çalıştığını onaylamak,
· Hataları ve kusurları bulmak ve önlemek,
· Yetersiz yazılım ve kalite riskini azaltmak,
· Yasal standartlarına uymasını sağlamak bunlardan başlıcalarıdır.
Unit Test Nedir?
Unit test temel olarak; Bir yazılımın en küçük biriminin tek başına test edilmesidir. Unit Testleri bu birimleri izole eder ve doğruluğunu onaylar. Bu birim gerek bir fonksiyon, gerek bir fonksiyonun içindeki küçük bir iş yapan kod bloğu olabilir. Bir projede unit testlerin başarı ile sonlanması, ürünün son kullanıcı tarafında da sorunsuz çalıştığı anlamına gelmez. Bu noktada Unit testin asıl amacı ve işlevi teknik ekip için bir dokümantasyon niteliğinde olmasıdır.
Doğru unit test nasıl yazılır?
Unit testin nasıl yazılması gerektiği de çok önemlidir. Doğru yazılmayan birim testler bize hiçbir şey kazandırmayacağı gibi en ufak değişiklikte hatalar vermeye başlayıp başımızı ağrıtırlar. Üstüne bir de testlere bakım yapmakla uğraşmak zorunda kalacağımız için de fayda sağlamanın aksine zararlı olabilirler.
O yüzden unit test yazarken aşağıdaki noktalara dikkat etmekte fayda var;
Tek bir şeye odaklanın: Her testin tek bir şeyi test ettiğinden emin olun. Çok gerekli değilse aynı test içerisine birden fazla assert ifadesi koymayın.
Bağımlılıkları (dependency) değil, tek bir sınıfı test edin: Yazıda daha önce de değindiğimiz gibi, bir sınıfı test ederken o sınıfı bağımlı olduğu diğer yazılım bileşenlerinden izole edin, aksi taktirde yazdığınız test birim test değildir.
Yazdığınız testler birbirini etkilemesin: Yazdığınız her test birbirinden bağımsız bir şekilde tek başına sorunsuz çalışabilmelidir. Eğer yazdığınız bir birim test başka bir birim testin üreteceği veriye bağımlıysa yanlış yapıyorsunuz demektir.
Testlerinizi doğru isimlendirin: Test sayısı arttıkça isimlendirmenin önemi de artar. Kafa karıştırıcı test isimleri kullanmak ileride problemlere yol açar. Açıklayıcı olması için test isimlerini uzun tutmanız gerekiyorsa öyle yapın, uzun isimler yanlış isimlerden daha faydalıdır.
Test koduna ikinci sınıf kod muamelesi yapmayın: Testler de yazılımın bir parçasıdır. Dolayısıyla normal program kodunu yazarken ne kadar özen gösteriyorsanız test kodlarına da aynı özeni gösterin, kod tekrarlarından kaçının, okunabilir test kodu yazın.
Unit Testler hata bulmak için değildir.
Bir yazılım sistemindeki hataları (bug) bulmak unit testler ile mümkün değildir. Çünkü unit testlerin yaptığı iş yazılımın en küçük parçalarını kendi içerisinde test etmektir. Bir yazılım sistemi, onu oluşturan parçaların toplamından çok daha fazlasıdır. Dolayısıyla bu bütünü test etmek için farklı yöntemler kullanmak gerekir. İşlevsel test (functional testing), bütünleştirme testi (integration testing) bunlara örnek verilebilir ancak konumuz unit test olduğu için bunlara değinmeyeceğiz.
Hataları bulamıyorsa Unit Testler ne işe yarıyor?
Unit test yazmanın sağladığı gerçek fayda, bizi kaliteli kod yazmaya teşvik etmesidir. Peki bu nasıl olur? Öncelikle şunu söylemek gerekir ki, unit test yazmanın birinci kuralı test etmekte olduğumuz sınıfı, bağımlı olduğu diğer bütün bileşenlerden izole etmektir. Örnek verecek olursak, test ettiğiniz sınıfın bir Google servisine bağlanarak veri çektiğini düşünün. Ancak unit test esnasında bu sınıfın Google servisine bağlanıp veri çekmesini istemeyiz. Çünkü unit testinin amacı yazılımın Google servisleriyle çalışabildiğini kanıtlamak değildir. Unit test yazarken, bağlantılı olduğumuz diğer bütün parçaların sorunsuz biçimde çalıştığını varsayarak yazarız, çünkü odaklandığımız şey sınıfın kendisidir, bağımlı olduğu diğer bileşenler değil. Bu varsayımı yapabilmek için de, mocking dediğimiz tekniği kullanarak test esnasında gerçek Google servisine bağlanmak yerine bizim yarattığımız sahte bir servise (mock object) bağlanıp sınıfın ihtiyacı olan veriyi döndürürüz. Bu şekilde test ettiğimiz sınıf dışarıda bir servise bağlanmadan ihtiyacı olan veriyi alır ve işletimini tamamlar.
Mock’lama Nedir?
Mock tamamen beklentilerimiz ile ilgili. Örneğin eğer servis katmanımızı test ediyorsak, verinin nereden yada nasıl geldiği bizim pek umurumuzda olmuyor. Aynı şekilde MVC tasarım şablonunda, Controller’ımızı test ediyorsak sınıfımızın servis üzerinden ne şekilde konuştuğu da bizi pek ilgilendirmiyor. Tabi’ki bu kısımları da test etmemiz gerekiyor fakat onları ayrı unit testler de zaten test ediyoruz. İşte bu gibi durumlarda, ilgili katmanı mock’luyoruz. Mocklarken yaptığımız aslında basitçe ne mockladığımız methodun hangi tür çağırıldığında ne şekilde davranmasını beklediğimizi belirtmektir. Bunu yapan bir çok kütüphane olmasına karşın benim bu yazıda üzerinde konuşacağım kütüphane Mockito olacak.
Mockito Nedir?
Mockito, Java’da test sınıflarımızı yazarken kullanabileceğimiz bir mock kütüphanesidir. Bu kütüphane ile Java test metotlarımızda test ettiğimiz senaryoların bağımlılıklarını yönetebiliriz. Mockito kütüphanesi kolay öğrenilebilirliği ve kolay kullanılabilirliği ile Java’da en çok kullanılan mock kütüphanesidir.
Mockito’nun Avantajları:
* Mock nesneleri kendimizin yazmasına gerek kalmaz.
* İnterface methodlarının yeniden adlandırılması veya parametrelerinin yeniden sıralanması, Mock’lar çalışma zamanında oluşturulduğundan test kodunu bozmaz.
* Return değerlerini destekler, exception’ları destekler, Method çağrılarının sırasını kontrol etmeyi destekler, Mock oluştururken Annotation kullanmayı destekler.
Sonuç olarak, okunması, anlaşılması ve değiştirilmesi daha kolay olan daha basit bir test kodu sağlar. Mockito’yu JUnit ve TestNGgibi diğer test frameworkleri ile de kullanabiliriz.
Şimdi de mockito kullanarak bir unit test örneği kodlayalım.
Örneğimizde authentication benzeri bir yapı kuracağız.
Öncelikle bu yapıyı kurmaya User classımızı oluşturarak başlıyoruz.
UserDao adında, Userlar hakkında database’de çeşitli işlemler yaptığımız bir Dao servis classı oluşturuyoruz. Örnek olduğundan şimdilik bir List ile sınırlandırıyoruz. “findUserByName” fonksiyonu çağrılan isimdeki User’ı List’den return etmekte görevli.
Şimdide test edeceğimiz LoginService sınıfını yazıyoruz.
Şimdi sıra geldi LoginService için Unit Test yazmaya. Burada benim UserDao’ya bir bağlılığım olacak yani LoginService için bir test yazmak istediğimde UserDao’yu kullanacağım. Fakat ben hiç UserDao sınıfını karıştırmayıp UserDao sınıfını mocklayarak sadece LoginService için testler yazabilirim.
Evet, görüldüğü üzere userDao classını Mockladık ve “when().thenReturn()” kullanarak istediğimiz değeri return etmesini sağladık. Böylelikle başarılı bir şekilde Unit Testimizi yazmış olduk.
Detaylı şekilde çalıştırarak incelemek isterseniz, github linki:
https://github.com/Rossven/MockitoTest
Bu yazıyı biraz giriş niteliğinde olarak düşünebilirsiniz. Bir sonraki yazıda görüşmek dileğiyle, hoş kalın :)