TDD ve birim testi: test yazarak güvenli kod geliştirme
Test güdümlü geliştirme döngüsünü, iyi bir birim testinin özelliklerini, mock kullanımını ve sık hataları örnek bir kodla öğrenin.
TDD ve birim testi nedir?
Birim testi (unit test), kodun en küçük parçasını, örneğin tek bir fonksiyonu ya da sınıfı, dış bağımlılıklardan ayırarak doğrulayan otomatik bir testtir. Hızlı çalışır, tek bir davranışı kontrol eder ve başarısız olduğunda hatanın nerede olduğunu gösterir.
Test güdümlü geliştirme (TDD), bu testleri koddan sonra değil, koddan önce yazma yaklaşımıdır. Önce henüz var olmayan davranış için başarısız bir test yazılır, sonra o testi geçirecek en küçük kod yazılır, en son kod düzenlenir. Böylece her satır, onu gerekli kılan bir test tarafından korunur.
İkisi aynı şey değildir. Birim testi bir test türüdür; TDD ise bu testleri geliştirme sürecinin sürücüsü yapan bir çalışma biçimidir. Birim testi yazıp TDD uygulamamak mümkündür; ama TDD uygulamak birim testi yazmayı gerektirir.
Red, green, refactor döngüsü nasıl işler?
TDD'nin kalbi kısa bir döngüdür:
- Red: Henüz çalışmayan bir davranış için test yaz ve testin başarısız olduğunu gör
- Green: Testi geçirecek en küçük, hatta en basit kodu yaz
- Refactor: Testler yeşilken kodu temizle, tekrarı kaldır, adları düzelt
Döngü çok kısa tutulur; bir test, bir küçük kod, bir temizlik. Başarısız testi görmek önemlidir, çünkü testin gerçekten bir şeyi kontrol ettiğini kanıtlar. Hiç kırmızı görmediğiniz bir test, yanlış ya da boş olabilir.
Refactor adımı kolayca atlanır, oysa TDD'nin asıl kazancı oradadır. Testler güvence verdiği için tasarımı bozma korkusu olmadan kodu iyileştirebilirsiniz.
İyi bir birim testi nasıl yazılır?
İyi bir birim testi hızlıdır, bağımsızdır, tekrarlanabilirdir ve tek bir davranışı kontrol eder. Testin başka bir testin sırasına ya da çalıştırıldığı ortama bağlı olmaması gerekir.
Testi okunur kılan yaygın kalıp üç parçadır: hazırlık (Arrange), eylem (Act) ve doğrulama (Assert). Önce gerekli nesneler hazırlanır, sonra test edilen eylem çalıştırılır, en sonda sonuç beklenenle karşılaştırılır. Test adı davranışı anlatmalıdır; "kuponlu siparişin toplamı indirimli hesaplanır" gibi bir ad, testin neden kırıldığını ilk bakışta söyler.
Birim testinde dikkat edilecek noktalar:
- Bir testte tek bir davranış doğrula
- Uygulamanın iç ayrıntısını değil, dışarıdan görünen davranışı test et
- Sınır durumlarını ve hata yollarını da dene
- Test verisini sade ve anlaşılır tut
Bir testin kalitesini sınamak için basit bir yöntem vardır: testin geçtiği kodda küçük bir hata yaratın, örneğin bir koşulu tersine çevirin. Test kırılmıyorsa gerçek bir şeyi doğrulamıyor demektir. Test adlarını da aynı gözle okuyun; bir ekip arkadaşı yalnızca test adından hangi davranışın bozulduğunu anlayabiliyorsa test iyi adlandırılmıştır.
Mock, stub ve test izolasyonu
Bir fonksiyon veritabanı, ağ çağrısı ya da saat gibi dış bağımlılıklara dayanıyorsa test yavaşlar ve kararsızlaşır. Test izolasyonu, bu bağımlılıkları kontrollü sahte nesnelerle değiştirerek yalnızca test edilen birimin davranışını görmeyi sağlar.
Sahte nesnelerin türleri vardır. Stub, önceden belirlenmiş bir yanıtı döndürür. Mock ise yalnızca yanıt vermekle kalmaz, belirli çağrıların yapılıp yapılmadığını da doğrular. Hangisinin ne zaman kullanılacağı konusunda ölçü şudur: sonucu doğruluyorsanız stub yeterlidir; bir yan etkinin gerçekleştiğini doğrulamanız gerekiyorsa mock kullanılır.
Aşırı mock kullanımı testi koda yapışık hale getirir; iç ayrıntı değişince test kırılır, ama davranış değişmemiştir. Bu yüzden yalnızca gerçekten dış sınırdaki bağımlılıklar sahtelenir.
Örnek: sepet toplamı hesaplayan bir fonksiyon
Örneğin bir sepet toplamı hesaplama fonksiyonu yazılacak. İlk test boş sepetin toplamının sıfır olduğunu kontrol eder ve başarısız olur, çünkü fonksiyon henüz yoktur. En basit kod yazılır, test geçer.
Sonraki test tek ürünlü sepeti, ardından birden fazla ürünlü sepeti kontrol eder. Her seferinde yeni test önce kırılır, sonra kod genişletilir. Sonra kupon kodu eklenir: geçerli kupon toplamı düşürmeli, geçersiz kupon toplamı değiştirmemeli. Kuponu doğrulayan servis dış bağımlılık olduğundan testte stub ile sahtelenir.
Her yeşil aşamadan sonra kod düzenlenir; tekrar eden hesaplama yardımcı bir fonksiyona çıkarılır. Sonunda elinizde hem çalışan bir fonksiyon hem de davranışını anlatan bir test seti vardır. Bu set, ileride bir değişiklik yapıldığında hızlı bir güvenlik ağı olarak çalışır.
Bu örnekte sınır durumları da testle sabitlenir: negatif adet, aynı üründen birden fazla satır ya da yuvarlama davranışı. Her biri ayrı bir test olur ve fonksiyonun sözleşmesini dışarıdan okunabilir hale getirir.
Kapsam, kod kalitesi ve sık yapılan hatalar
Test kapsamı (coverage), testlerin kodun ne kadarını çalıştırdığını gösteren bir ölçüdür. Düşük kapsam, test edilmemiş bölgeleri gösterir; ama yüksek kapsam testlerin iyi olduğu anlamına gelmez. Hiçbir şeyi doğrulamayan bir test de satırı çalıştırır. Kapsamı hedef değil, boşlukları bulmak için bir araç olarak görmek gerekir.
Sık hatalar: testi koddan sonra yazıp yalnızca mevcut davranışı onaylamak, her şeyi mocklamak, çok büyük testler yazmak, yavaş testleri birim testi saymak, refactor adımını atlamak ve başarısız testleri kayıt dışı bırakmak.
Öğrenme yolu: önce bir test çerçevesinde basit birim testleri yazın, sonra red-green-refactor döngüsünü küçük bir alıştırmada deneyin. Ardından mock ve izolasyon tekniklerine, sonra eski kodu test altına alma ve refactoring'e geçin. Bir kod kata'sını farklı dillerde tekrarlamak döngüyü pekiştirir.
Bu beceriyi Mindset programlarında nerede çalışırsınız?
Mindset kataloğundaki Test Driven Development (TDD) ve Unit Testing Uzmanlığı Eğitimi'nin modül başlıkları şunlardır: Genel Bilgi, Test Driven Development (TDD) Temelleri, Unit Testing Temelleri, Mocking ve Test Isolation Teknikleri, Test Coverage ve Kod Kalitesi Analizi, Refactoring ve TDD ile Güvenli Kod Geliştirme, Uygulamalı Proje ve İleri Teknikler.
İlgili Mindset programı
Test Driven Development (TDD) ve Unit Testing Uzmanlığı Eğitimi
Sık sorulan sorular
TDD ile birim testi aynı şey midir?
Hayır. Birim testi, küçük bir kod parçasını dış bağımlılıklardan ayırarak doğrulayan test türüdür. TDD ise bu testleri koddan önce yazarak geliştirmeyi yönlendiren bir çalışma biçimidir. Birim testi yazıp TDD uygulamamak mümkündür, ama TDD uygulamak birim testi yazmayı gerektirir.
TDD her projede uygulanmalı mı?
Her zaman gerekmez. İş kuralı yoğun, değişikliğe açık ve uzun ömürlü kodda en çok faydayı sağlar. Kısa ömürlü bir deneme ya da atılacak bir prototipte test yazmanın maliyeti faydadan fazla olabilir. Ekip, kodun ömrüne ve değişim sıklığına bakarak karar vermelidir.
Mock ile stub arasındaki fark nedir?
Stub önceden belirlenmiş bir yanıtı döndürür ve sonuca bakılarak test edilir. Mock ise belirli çağrıların yapılıp yapılmadığını da doğrular. Yan etkiyi doğrulamak gerekiyorsa mock, yalnızca girdiyi sağlamak gerekiyorsa stub tercih edilir.
Yüksek test kapsamı kodun kaliteli olduğunu gösterir mi?
Tek başına göstermez. Kapsam, kodun testlerce çalıştırıldığını gösterir, doğrulandığını göstermez. Anlamlı doğrulama içermeyen testler de kapsamı yükseltir. Kapsamı test edilmemiş bölgeleri bulmak için kullanın; testlerin anlamlı doğrulamalar içerip içermediğini ayrıca değerlendirin.
TDD hangi programlama dilleriyle yapılır?
Yaygın modern dillerde bir test çerçevesi bulunur, bu yüzden TDD dilden bağımsız bir yaklaşımdır. Önemli olan döngüyü uygulamak ve testleri hızlı çalıştırabilmektir; çerçeve seçimi projenin diline göre belirlenir. Dilin tip sistemi ve bağımlılık enjeksiyonu desteği testlenebilir kod yazmayı kolaylaştırır ya da zorlaştırır; bağımlılıkları dışarıdan verilebilir yapmak hangi dilde olursa olsun izolasyonu kolaylaştırır.
Kategori: Yazılım Geliştirme