Birim (Unit) Test

Mehmet Ali EROL
Ford Otosan
Published in
4 min readMay 2, 2023

Çalışır bir ürün oluşturmak için; farklı birçok küçük parça ya da işlevi bir araya getirmemiz gerekir.

Bu yazıda bir bütünü ortaya çıkaran en küçük birimleri (unit) test etmeyi ve test ederken kullandığımız stub, mock, spy, fake gibi yöntemleri ele alacağız.

Bir araç üzerinden bu konuyu ele alalım.

Araç üretirken yüzlerce belki binlerce parça bir araya getirilir ve bu parçaların bir ahenk içinde çalışması beklenir. Farlar, sinyaller, emniyet kemeri, direksiyon, klima … vb tüm bu parçaların doğru çalışıp çalışmadığını anlamamız önemlidir.

Peki ürettiğimiz bu aracın tüm özellikleri doğru çalışıyor mu? 🤔

Ürettikten sonra bir bütün olarak ele alıp her bir aracı uçtan uca test etmemiz mümkün olmadığı için testler, birimler(units) özelinde yapılır.

Örnek olarak farlar daha araca takılmadan önce test edilebilir. Böylece araca takıldığında çalışacağından emin olabiliriz. Buna birim testi diyebilir ve yazılım dünyasında küçük sınıflarımızın test edilmesine benzetebiliriz.

Peki bir farı test edebilmek için nelere ihtiyacımız var? Bağımlılıklarımız neler?

Elektriği en önemli bağımlılığımız olarak düşünebiliriz. Normalde araçlarımızdaki farlar bir aküden güç alır. Bir buton kullanarak bu farların aküden aldıkları elektrik ile yanmalarını sağlarız. Peki test ederken elektriği nasıl sağlayacağız?

Burada karşımıza yazılım dünyasında test doubles denilen kavram çıkıyor. Bunları test bağımlılık yardımcıları olarak düşünebiliriz. Bağımlılık olduğu kısımlarda amacımız komple sistemi test etmek değil de birimi test etmek olduğu için bazı bağımlılıkların varmış gibi gösterilmesi gerekir.

Yukarıdaki örnekte amacımız farın yanıp yanmadığını görmek. Bunun için bir aküye daha doğrusu elektriğe ihtiyacımız var. Elektriği yazılım tarafında dataya benzetelim. Yukarıdaki örneği veri tabanından veri alıp ekrana sonuç yazdıran bir fonksiyon olarak ele alalım. Bu durumda akü -> veritabanı, far -> ekrandaki çıktı ve elektrik -> data olmuş olacak.

Birim testinin çok hızlı çalışması gerekir, çünkü bu testlerden binlercesini yazarız. Geliştirme döngümüzün hızı için her bir testin milisaniyeler içinde tamamlanması önemlidir. Her test 1 saniye de çalışsa 1000 testi çalıştırmak için ciddi bir süre beklememiz gerekir.

Peki örneğimize geri dönelim. Her seferinde bir akü bulup sonrasında onu fara bağlamak ve sürekli o akünün dolu olmasını sağlamak gibi bir derdimiz olacak. Unutmayın amacımız aküyü test etmek değil sadece farı test etmek istiyoruz. Bu durumda akü kullanmak yerine aküymüş gibi davranacak bir yardımcı araç kullanıyoruz. Bize istediğimiz elektriği istediğimiz zaman çok hızlı bir şekilde verecek bir araç ile bu bağımlılığı ele alıyoruz. Bu araca yazılım dünyasında stub diyoruz. Stub, veri döndüren bağımlılıkların simüle edilmesi için kullanılan sınıflara verilen isimdir.

Yine yukarıda verdiğimiz örnekte olduğu gibi veri tabanına gidip verileri alacak bir servis yerine stub bir sınıf oluşturarak sanki veri tabanından geliyormuş gibi cevap vermesini sağlıyoruz. Bu cevap veri tabanına gitmekten kat kat daha hızlı şekilde bize geliyor.

Bir de farı açmak için kullandığımız anahtarın testini düşünelim. Anahtarı açık konuma getirdiğimizde farlar yanmalı ve kapalı konuma getirince de farlar kapanmalı. Bu durumda bağımlılığımız far oluyor ancak farı farklı bir senaryo ile test etmiştik. Şimdi sadece anahtarı test etmek istiyoruz. Hemen far bağımlılığımızı simüle edecek bir sınıf oluşturuyoruz ve far yak özelliğini kodluyoruz. Yukarıdaki örnekten farkı; far yanmak için aküden elektrik talep ediyordu ve bu bağımlılığı stub ile geçmiştik. Burada ise far anahtarı komut veren tarafta yani bir çağrı yapıyor. Bu durumda da mock sınıflardan yararlanıyoruz. Mock sınıflar, genelde içine veri alan metotları içerir. Çağrılması gereken bağımlılığın doğru şekilde çağrılıp çağrılmadığını kontrol etmek için kullanılır. Bu konuda farklı fikirler olsa da genel olarak davranışı test etmek için kullanılmaktadır. Test aşamasında stub sınıflara göre daha esnek alt yapılar sağlayabilirler. (Şu değeri verirsem bu sonucu üret, böyle yaparsam exception fırlat vs gibi ayarlamalar test senaryoları içerisinde yapılabilir)

Günümüzde çoğu araçta otomatik yanan farlar mevcut. Araç gün ışığı algılandığında farlarını kapatıyor ve karanlıkta otomatik olarak açıyor. Bu kararı veren parçayı test etmek istediğimizde far bizim için yine bir bağımlılık oluyor ancak bu sefer mockdan farklı olarak kaç kez farın açılıp kapandığını görmek ve bu konuda veri toplamak istiyoruz. Bu senaryoda spy türünden sınıfları kullanabiliriz. Spy sınıflar, mock sınıflardan farklı olarak bilgi toplamak ve toplanan bilgi üzerinden kontroller yapmak için kullanılırlar. Mesela mail gönderme işlemi için bir spy tanımlayabilir ve kaç kez mail atıldığını tutabiliriz. En son kısımda, atılması gereken mail sayısını test sonunda kontrol edebiliriz.

Son olarak aracın viteslerini kontrol etmek istiyor olalım, her bir aracı alıp uzun bir yola çıkıp son vitese kadar gelmeyi denemek oldukça zaman alan bir test olacaktır. Bu nedenle bu iş için özel alt yapılar kullanılır. Bu yapılar sanki araç yolda gidiyormuş gibi bir atmosfer sağlar. Roll test sırasında, aracın tekerleri iki silindir arasına yerleştirilir ve tekerler döndükçe silindirlerde dönerek aracın durduğu yerde yüksek hızlara ulaşması sağlanabilir. Bu senaryoyu yazılım dünyasında bir api çağırmaya benzetelim. Gerçekten apiyi çağırmak yani uzun bir yolda hızla gitmek yerine fake bir sınıf oluşturup sanki apiye gidiyormuş gibi yapabiliriz. Bunu gerçeğe en yakın birim test yöntemi olarak düşünebilirsiniz.

Yukarıdaki kavramları ezbere bilmenize gerek yok. Kavramlar konusunda fikir sahibi olmak, özellikle ekip olarak geliştirilen unit testlerde aynı dili konuşmamıza imkan sağlayacaktır.

Birim test yazabilmek için öncelikle birim teste uygun kod yazmak kritiktir. Olabildiğince küçük metotlar, düşük bağımlılıklar ile geliştirme yapmalıyız. Geliştirdiğimiz metotların sağlamasını ise çok hızlı çalışacak ve bağımlılıkları doğru şekilde simüle edecek unit testler ile sağlamalıyız.

Birim test genelde ekstra maliyet görülür bu nedenle birçok yazılımcı uygulamaz. Uygulayanların bir kısmı da kod analiz araçları (static code analysis tools) üzerinde kapsama oranı (code coverage) yüksek çıksın diye dikkat etmeden yazar. Bu gibi nedenler ile testlerden alınabilecek verim maalesef alınamamaktadır.

Her bir birimin kendisinden bekleneni verip vermediği kontrol edilmelidir. Hataları daha kullanıcıya gitmeden tespit etmek mümkündür. Küçük uygulamalarda bu durum birim testler olmadan bir şekilde yönetilebilirken, uygulama büyüdükçe içinden çıkılmaz bir hal alır. Yaptığınız bir geliştirmenin nereleri etkilediğini bilemeyeceğimiz için sıklıkla bug yönetimi ile uğraşmak zorunda kalırız ve müşteri memnuniyeti düşer.

Davranışı test etmenin yanında performansı da test etmek önemlidir. Konu ile ilgili yazıma aşağıdaki link ile ulaşabilirsiniz.
https://medium.com/ford-otosan/kod-performans%C4%B1-nas%C4%B1l-%C3%B6l%C3%A7%C3%BCl%C3%BCr-benchmarkdotnet-75148d546793

--

--

Mehmet Ali EROL
Ford Otosan

Software Developer, Chapter Lead, Impovisional Theater Actor