Test Yazmak — 1

Fatih Küçük
LCW Digital
Published in
4 min readAug 27, 2021

Giriş

Bu yazıda test yazma ile ilgili genel bilgileri aktarmaya çalışacağım. Temel kavramların üzerinden geçtikten sonra, test yazarken işinizi kolaylaştıracağını düşündüğümüz bazı ipuçlarını paylaşacağım. Daha sonra kod geliştirirken uymanızı tavsiye ettiğimiz bazı konuları aktarıyor olacağım.

Örneklerimizde .net Core 3.1, test için xUnit tercih edildi. Ayrıca assertion için FluentAssertions kütüphanesini yardımcı olarak kullandık. Dil ve kütüphanelere çok takılmayalım. Test yazmak isteyenlere kolaylık olması açısından genel kavramları açıklamak istiyorum sadece.

Neden test yazmalıyız?

Test yazmak genelde kod geliştirici için, maalesef, gereksiz bir uğraş gibi gözükür. Teste ayrılacak olan eforu, asıl işi yapan kodu yazmaya ayırmak daha mantıklı gibi gelir. Projenin ilk başında, henüz kod satır sayısı artmamışken, doğru bir yaklaşım gibi gelse de; proje büyüdükçe, kod bakımı (maintenance), modernizasyon ve refactoring yapmak imkansız hale gelir. Çünkü bir yazılımcının bunları yapmaya cesareti yoktur, artık. Her yeni bir özellik eklenmesi, her dağıtım kabus haline gelir. Çünkü, yapılacak değişikliğin nereleri etkileyeceğini, nelere malolacağını kimse kestirememektedir. Burada da, yapılacak olan değişikliğin gerekliliği önem kazanır. Yani alınacak riske değecek iyileştirmeler yapılabilir ancak.

Test Yazmak Zordur!
Aslında test yazmanın kendisi değil, test edilebilir kod yazmak zordur. Kod ne kadar soyutlaştırılabildiyse, ne kadar az kompleksse ve OOP prensiplerine ne kadar fazla hassasiyet gösterildiyse, test yazmak da o kadar kolay olur.

Test yazarken en fazla vakti “test yazılabilecek ortam”ın ayarlanması alır. Bu da daha başlamadan insanı test yazmaktan soğutabilir. Bu yazı serisinde bunu nasıl yapabileceğinizi sizlere gösteriyor olacağım.

Aslında her geliştirici, yazmış olduğu kodu test eder. Bir ekran geliştirirken; uygulamayı çalıştırıp, login olup, menülerden ilgili ekranı bulup, ilgili parametreleri girip beklenen sonucu üretip üretmediğini elle test ederiz. Bir hata aldığımızda, veya farklı bir geliştirme yaptığımızda bu adımları tekrarlarız. Hatta yeni eklediğimiz kodun, daha önceki geliştirmeleri bozup bozmadığını anlayabilmek için, daha önce yaptığımız manuel testleri tekrar tekrar yapmak zorunda kalırız. Aslında elle yaptığımız her development testinde bir değer üretiriz. Fakat bu kıymetli değer kayıt altına alınamadığı için kaybolur. Test yazarak hem bu değeri kayıt altında alarak kaybetmemiş, hem de elle yapılan development testlerini otomatikleştirmiş oluruz. Ayrıca zaman içinde çok değerli bir test ambarımız oluşmuş olur.

Test Çeşitleri

Geliştirici testleri; kapsamına, çalışma süresine, yazılış tarzına göre farklı isimler alabiliyor. Çeşitli kaynaklarda farklı tanımlar bulunuyor. Ayrıca bu tanımların sınırları da çok sert çizgilerle belirlenmiş değil. Biz test metodlarımızı aşağıda tanımlamış olduğumuz şekilde kategorize ediyor olacağız. Bu arada çok daha granül isimlendirmeler de mevcut. Fakat biz kavram kargaşası yaratmamak için şimdilik aşağıdaki test çeşitleri ile yetiniyor olacağız:

Birim Testleri
Burada amaç, test edilmek istenen şeyi tüm bağımlılıklarından arındırarak test etmektir. Nesne Yönelimli yaklaşımda “birim” olarak “sınıf” kabul edilmesi mantıklıdır. Yani birim test yazarken test ettiğimiz sınıfı veya sınıfın metodunun yaptığı işi test edecek kod yazmalıyız. Bu sınıf veya metod başka bir sınıfa veya başka bir komponente erişiyor ise, bunları moklama gibi yöntemlerle pas geçmeliyiz.

Entegrasyon Testleri
Birden fazla sınıf veya katman (veri tabanı, api vs) bir arada test ediliyorsa, bunu entegrasyon testi olarak adlandırabiliriz. Bir WebAPI’nin data katmanı ile olan entegrasyonu gibi.

Komponent Testi
Birden fazla komponentin bir arada çalışıp çalışmadığını anlamak için yaptığımız testlerdir. Bir sistemin uçtan uca çalışıp çalışmadığını gösterir.

Test Piramidi olarak bilinen yaklaşıma göre; birim testleri en altta, entegrasyon testleri onun üzerinde, bizim burada bahsettiğimiz komponent testleri de hepsinin üstünde yer alır. Piramitin en altından yukarı doğru çıktıkça, testlerin yazılma, bakım ve çalışma süreleri artar. Aynı şekilde piramitin tepesine doğru entegrasyon derecesi de artar.

Test Piramidi

Test Kavramları (Terminoloji)

İsimlendirme bizim için bir araç evet, fakat iletişimi kolaylaştırmak adına aşağıdaki kavramlara aşina olunmasında fayda olduğunu düşünüyorum.

Bir test metodu üç parçadan oluşur:
Arrange: Test ortamı hazırlanır. Test edeceğimiz metodun veya endpointin parametreleri ayarlanır.
Act: Burada test işlemleri gerçekleşir. Test edeceğimiz metod veya servis çağırımı yapılır.
Assert: Test dünyasında “expected: beklenen” ve “actual: aslında olan” diye iki kavram vardır. “Expected” yaptığımız test sonucunda beklediğimiz sonuçtır. “Actual” ise, test sonucunda ortaya çıkan sonuçtur. Assert adımında bu iki değerin eşit olup olmadığı kontrol edilir. Eşit ise testimiz başarı ile geçmiş demektir. Bizden mutlusu yok.

Mock: Belli şartlar alında istediğimiz sonuçları üreten önceden programladığımız objelerdir.

Dummy: Program çalışırken kullanılmayan değişkenlere verilen isimdir. Mesela bizim testimiz için hiç kullanılmayan bir constructor parametresini, test etmek istediğimiz sınıfı ayağa kaldırmak için boş olarak geçebiliriz.

Fake: Canlı ortamda kullanılamayan fakat çalışır durumdaki parçalardır. “in memory db”ler buna örnek verilebilir.
Bu kavramların detaylı açıklamasına ve burada değinilmeyen diğerleri için şu makaleyi okuyabilirsiniz: https://martinfowler.com/articles/mocksArentStubs.html

Code Coverage: Basit anlamda, yazmış olduğumuz testlerin gerçek işi yapan kodun ne kadarını kapsadığını belirten bir metriktir. Ne kadar büyük ise o kadar iyidir. Yeni test yazmaya başlayan ekipler için düşük bir değer belirlenip, daha sonra test yazma alışkanlığı yerleşince bu yükseltilebilir. Bu, testlerimizin yazmış olduğumuz kodun %80'ini dolaştığını göstermektedir.
Coverage’ın çok yüksek olması (mesela %100) o koddan hiç bir şekilde bug çıkmayacağı anlamına gelmez. Teorik olarak, bir uygulamadaki bugları sıfıra indirmek mümkün değildir. Coverage’ı yüksek tutmak bug çıkma ihtimalini azaltır. Burada test yazan kişinin yetenekleri ön plana çıkmaktadır.

Peki coverage kaç olmalı? Bunun cevabını şuradaki anektod çok güzel anlatmış: http://www.developertesting.com/archives/month200705/20070504-000425.html

Coverage’ı test edilmeyen kodları tespit etmek için kullanmak daha mantıklıdır.

Serinin bu ilk yazısı ile test dünyasına bir giriş yapmış oldunuz. Örnekler ile devam ediyor olacağım.

--

--

Fatih Küçük
LCW Digital

Software Architect @ LC Waikiki - All opinions are my own.