Geçenlerde yine tartışması yapılıyor: private olan metotları nasıl test ederiz? Benim cevabım: “edemeyiz!” Karşıdan gelen cevap: “dediğim gibi, her şeyi test etmek mümkün değil demek ki!“. Benim cevabım: “her şeyi test etmek mümkün, private’i protected yaparsın, olur, biter.” Karşı tarafın cevabı: “kardeşim ortada OOP diye bir şey var, kafana göre nasıl öyle private bir metodu proteted yaparsın?“. Yaparım canım kardeşim. OOP’yi filan takmam! Protected‘de yaparım, public‘de. Bir sınıfı test edebilmek için her türlü yöntemi kullanırım, gözünün yaşına bakmam. Bu uğurda her şey mübahtır.
Bu bazıları için çok radikal bir yaklaşım tarzı gibi görünebilir. Ama uygulama çalışırken bir sebepten dolayı patladığı zaman kimse gelip size, bu sınıfı neden OOP’ye uygun tasarlamadınız diye sormaz. Bu sınıf neden patladı, sizin testleriniz yok mu diye sorar! O zaman da “bu metot private idi, o yüzden test yazamadım” mi diyeceksiniz? Pek ikna edici değil, değil mi?
Bir uygulama geliştirken bir kod biriminin test edilebilirliğinin önündeki engel OOP prensipleri olmamalıdır. Eğer öyle ise, OOP prensipleri yanlış uygulanmış demektir. OOP kod geliştirmek için kullanılan bir araçtır. private yerine göre kullanılabilecek bir erişim mekanizmasıdır. Lakin private olan bir metot benim için yoktur, yani test edilmesi mümkün değildir. Benim için test edilemeyen kod birimi olamayacağı için, private ile tanımlanmış metotları yok sayarım. Test edilemeyen bir metodun başına her türlü iş gelebilir. Benim bu tür metotları görünce ilk yaptığım şey, metodu hemen protected yapmak ve o metodu izole bir şekilde test eden bir birim testi yazmak olur. Protected yetmedi ise public yaparım. Nihai amaç metodu bir şekilde test edebilmektir.
Ne test edilir, ne test edilmez, nasıl test edilir tartışmalarının sonu gelmez ne yazık ki. Geçenlerde yine kocaman, ne yedüğü belli olmayan bir sınıfa yeni bir özellik eklemem istendi. Eğer gerekli kodu o sınıfa direk ekleseydim, yeni eklediğim kod birimini test etmem çok zor olacaktı, bunu biliyordum. Sınıfın tümünü test edecek bir birim testi oluşturmak için vaktim yoktu. Bende bunun yerine yeni bir static iç sınıf oluşturup, kodu bu sınıfın bir metodu olarak geliştirdim ve bir birim testi ile test ettim. Testin başarılı olduğunu gördükten sonra ne yedüğü belirsiz diye tabir ettiğim sınıfın gerekli metoduna oluşturduğum yeni sınıfın bir nesnesini yerleştirerek, static sınıfın metodunu orada koşturdum. Böylece ne yedüğü belirsiz sınıf dolaylı bir şekilde yeni oluşturduğum kod birimine sahip oldu. Doğal olarak gelen ilk soru şu oldu: “neden static bir iç sınıf oluşturdun ki? Orada bu işi görecek bir metot vardı, o metoda kendi kodunu ekleyebilirdin“. Eklerdim, eklemesine, lakin bu sınıfa eklediğim kodu nasıl test edebilirdim? Edemezdim. Sınıf için daha önce hiç birim testi yazılmamıştı. Sınıfa eklediğim kod birimini test edebilmek için tüm uygulamayı ayağa kaldırıp, benim eklediğim kod birimi çalışıyor mu diye otomatize edilemeyecek türden bir test yapmam gerekirdi. Yok efendim. bu tür yöntemler sınıf enflasyonuna sebep oluyormuş. Olursa, olsun, sorun nedir? Önemli olan benim yeni kod birimini izole bir şekilde test etmemdi.
OOP, tasarım şablonları ya da tasarım prensipleri sadece test edilebilirliğin emrinde çalışabilecek erlerdir, tersi değil! Bu saydığım araç, gereçlerin hepsinin efendisi test edilebilirlik özelliğidir. Bunların arkasına saklanıp, bu kod birimi test edilemez diyen yazılımcı kendisine ve müşterisine ihanet içinde olur.
EOF (End Of Fun)
Özcan Acar
Java için private methodları test edebilir aslında. Mockito’nun üzerine yazılmış powermockito ile private method invoke edilebiliyor hatta private methodlar da mocklanabiliyor ki private methodların çağırıldığı public methodlarda bir de onlar için tekrar tekrar upraşmayalım diye. Kendi sayfasında da çok iyi olmasa da dökümanları mevcut. http://code.google.com/p/powermock/
Birim testinin okunabilirligi ve sadeligi adina private metodu protected ya da public yapmayi tercih ederdim. Reflection kullanilarak ta private metotlar test edilebilir. Birim testleri Junit ve Hamcrest harici hicbir catiya bagimli olmadan calisabilmeli.
Elinize sağlık hocam güzel bir yazı olmuş,özelliklede test edilememiş bir kodun müşteriye ihanet olduğu konusunda sizinle aynı düşünceleri paylaşıyorum
Fakat test edebilmek için metodun erişim anahtarının değiştirilmesi çokda caiz olmamalı. Metodun gizlenmesi sadece bir yazılım metodolojisi için değil o kodun devamında üzerinden geçen insanlarada yol gösteriyol olmalı,yani kullanılmamasını veya önerildiği gib kullanılmasını sağlamalı kodcu. Böyle düşününce bence olması gereken bu kodun erişim düzeyinde değişiklik yapmak yerine , yapısal olan bir düzenleme
@ozcanacar test edebilmek uğruna her yol mübah ise eğer “Birim testleri Junit ve Hamcrest harici hicbir catiya bagimli olmadan calisabilmeli.” kısıtlaması olmamalı, o da mübah olmalı. nasıl olsa test için kullandığımız araçlar projeyi deploy ederken içeriğe dahil olmuyor.
eğer başka mimari ya da diğer kısıtlamalardan dolayı reflection ya da code junit dışındaki araçlar ile test edemiyorsak private metodu dışarıya açmaktansa sizin verdiğiniz örnekteki gibi statik yazarak test etmeyi tercih ederim. bir diğer seçenek de “tek sorumluluk prensibi”ni öne sürerim. her sınıfın tek bir görvi olacaksa private metod içinde yapılan iş de başka bir sınıfın görevi olmalıdır der, başka interface ve implementasyon ile yaratır test edeceğim sınıfta oradan kullanırım.
ayrıca Junit ve Hamcrest kullanmadığımızda da mocking yapamadığımız için unti testin asıl odaklanması gereken nokta olan test ettiğimiz sınıfın sormumlulukları ile değil, test ettiğimiz sınıfın ilişki içerisinde olduğu diğer sınıf/interfaceler ile de uğraşmak zorunda kalıyoruz. nacizane görüşüm unit test yaparken test edilen sınıf dışında ilişkide olduğu tüm sınıflar, statik metodlar dahi mocklanmalı dışarıdan soyutlanmalıdır. ayrıca bu sayede test ettiğimiz sınıfın ilişki içinde olduğu diğer sınıflara göre davranışları da kolayca simule edilebilir. örneğin jdbc kullanıyorsam test sınıfımda bağlantının kapanması durumunu test etmek için mocklamaktan başka çağrem yok jdbc’yi. aksi taktirde unit test değil integration/functional test yazmay başlarım.
neyse hocam, bu konuda sizin tezinize anti-tez bir blog yazısı da üşenmeyim kaleme alsam daha uygun olur gibi.
4-umut’un yorumuna tamamen katılıyorum.