Bölüm 1 / 12
Neden test
Güven ağı, regresyon, testin maliyeti.
15 dakikalık okuma
Buraya kadar kod yazmayı, tasarlamayı ve güvenli hâle getirmeyi öğrendin. Şimdi mesleğin en az konuşulan ama en çok ayırt eden sorusu: kodunun doğru çalıştığını nereden biliyorsun? "Elle denedim, çalışıyor" cevabı bugün için doğru olabilir; yarın başka bir geliştirici — ya da sen — o kodun üç satır ötesini değiştirdiğinde geçersizleşir. Otomatik test, bu sorunun tekrarlanabilir cevabıdır: bir kez yazılır, her değişiklikte saniyeler içinde yeniden sorar. Bu ders testin ne olduğunu ve ne olmadığını, neden yazıldığını, "zaman yok" itirazının neden çoğu zaman tersine işlediğini ve daha kod yazarken test edilebilirliğin nasıl bir tasarım özelliği olduğunu kurar. Hat boyunca xUnit, Moq, TDD ve entegrasyon testine ineceğiz; bu ders hepsinin zeminidir.
01Ne öğreneceksin
- Testin ne olduğu, ne olmadığı; testin hatanın yokluğunu kanıtlamadığı
- Regresyon kavramı ve testin bir güven ağı olarak asıl işlevi
- Kusur maliyeti eğrisi: hata ne kadar geç bulunursa neden o kadar pahalıdır
- "Test yazacak zaman yok" argümanının hangi koşulda yanlış, hangi koşulda haklı olduğu
- Manuel ↔ otomatik test ayrımı; keşif testinin yeri
- Test edilebilirlik bir tasarım özelliğidir — bağımlılık enjeksiyonuyla bağı
- Kırılgan test: implementasyona bağlanmanın bedeli
- Kapsama yüzdesinin neden tek başına ölçüt olmadığı; satır ↔ dal kapsaması, mutasyon testi
- Hangi kodun test edilmeyeceğine risk temelli karar vermek
02Test nedir, ne değildir
En yalın tanım: test, koddan beklediğin davranışı, yine kodla yazılmış ve otomatik çalıştırılabilir bir doğrulamaya dönüştürmektir. "Bu metoda 1000 TL borç ve 30 gün gecikme verirsem 1030 TL dönmeli" cümlesi bir beklentidir; test, bu cümlenin çalıştırılabilir hâlidir. Beklenti tutuyorsa test geçer, tutmuyorsa kırmızı yanar ve sana nerede kırıldığını söyler.
public class GecikmeZammiTests
{
[Fact]
public void OtuzGunGecikmede_BindeBirGunlukOranla_YuzdeUcZamUygulanir()
{
var hesap = new GecikmeZammi(gunlukOran: 0.001m);
var sonuc = hesap.Hesapla(anaPara: 1000m, gecikenGun: 30);
Assert.Equal(1030m, sonuc);
}
}
Sözdiziminin ayrıntısı 3. dersin işi; şimdilik şuna bak: bu on satır, "gecikme zammı doğru hesaplanıyor" iddiasını belgeleyen ve her çalıştırıldığında yeniden kanıtlayan bir kayıttır. İyi bir test paketi aynı zamanda kodun en güncel dokümantasyonudur — yorum satırı eskir, test eskiyemez, çünkü eskirse kırmızı yanar.
Ne olmadığı da en az bu kadar önemli:
- Test, hatanın yokluğunun kanıtı değildir. Dijkstra'nın klasik cümlesi: test hatanın varlığını gösterebilir, yokluğunu asla. Testler yalnızca sorduğun soruları cevaplar; sormadığın soru — düşünmediğin kenar durum — test paketinden geçip üretimde patlar. Bu yüzden test, kaliteyi garanti eden değil, kalite hakkında kanıt toplayan bir araçtır.
- Test, işin sonuna eklenen bir onay damgası değildir. "Kod bitti, şimdi test yazayım" yaklaşımı testi angaryaya çevirir; testin asıl değeri geliştirme sırasında verdiği geri bildirimdedir (5. derste TDD ile derinleşecek).
- Test, ayrı bir meslek grubunun tekelinde değildir. Test mühendisliği ayrı bir uzmanlıktır, ama yazdığı birimin testini yazmak geliştiricinin işidir. "Testçi bulur" kültürü, 4. bölümdeki maliyet eğrisinin en pahalı ucunda yaşamaktır.
03Regresyon ve güven ağı
Regresyon, önceden çalışan bir davranışın yapılan bir değişiklikle bozulmasıdır. Yazılımdaki hataların büyük bölümü yeni yazılan kodda değil, değiştirilen kodun dokunmadığını sandığı yerlerde çıkar: bir metodu hızlandırırsın, üç ekran ötede bir rapor yanlış toplamaya başlar. Kod birbirine bağlıdır; değişiklik dalga yayar.
Otomatik test paketinin asıl işlevi tam burasıdır: değişiklik yapma cesareti. Testleri olan bir kod tabanında refactoring (Hat 4) bir tuşa basıp sonucu görmekten ibarettir; testi olmayan tabanda her dokunuş risktir ve ekip zamanla dokunmamayı öğrenir. Dokunulmayan kod çürür: kimsenin anlamadığı, kimsenin düzeltmeye cesaret edemediği bölgeler büyür. Michael Feathers'ın mesleğe yerleşmiş tanımı bu yüzden serttir: "miras (legacy) kod, testi olmayan koddur" — yaşı ne olursa olsun. Dün yazdığın testsiz modül de bu tanıma girer.
Bu bakışla testin ekonomisi netleşir: test yazmak, gelecekteki her değişikliğin doğrulama maliyetini bugünden ödemektir. Elle doğrulama her değişiklikte yeniden yapılır ve maliyeti değişiklik sayısıyla çarpılır; otomatik doğrulama bir kez yazılır, çalıştırma maliyeti saniyelerdir. Uzun ömürlü, sık değişen kodda bu hesap her zaman otomasyonun lehine döner — kısa ömürlü kodda dönmeyebilir, buna 10. bölümde geleceğiz.
04Kusur maliyeti eğrisi
14-09'da güvenlik açıkları için kurduğumuz eğri, sıradan hatalar için de aynen geçerlidir: bir kusuru düzeltmenin maliyeti, kusurun bulunduğu aşama geciktikçe katlanır.
| Kusur nerede yakalandı | Düzeltme neye mal olur |
|---|---|
| Yazarken (derleyici, analizör, IDE) | Saniyeler; kimse fark etmez |
| Birim testte, commit'ten önce | Dakikalar; bağlam hâlâ kafanda |
| Kod incelemesinde / CI'da | Saatler; bağlam değişimi, gidiş-dönüş |
| Test ortamında, başka biri tarafından | Günler; kayıt, önceliklendirme, yeniden üretme |
| Üretimde | En pahalısı: acil yama, bozulan veriyi düzeltme, etkilenen kullanıcılar, itibar — kamu sisteminde yanlış hesaplanmış bir başvuru sonucu, idari itiraz ve düzeltme süreci demektir |
Eğrinin dikleşme nedeni teknik değil, insanidir: hata koddan uzaklaştıkça bağlam kaybolur. Yazdığın anda "bu satır neden böyle" sorusunun cevabı kafandadır; üç ay sonra üretim logundan gelen bir hata kaydında aynı cevap arkeoloji gerektirir. Ayrıca üretimdeki hata çoğu zaman veri bozar; kodu düzeltmek yetmez, bozulmuş kayıtları bulup onarmak gerekir — asıl fatura orasıdır. Testin ekonomik gerekçesi tek cümledir: test, kusuru eğrinin ucuz ucunda yakalama aracıdır.
05"Test yazacak zaman yok"
Bu cümleyi meslek hayatın boyunca duyacaksın; bazen sen kuracaksın. Argümanı ciddiye alıp parçalayalım, çünkü içinde hem bir yanılgı hem de bir gerçek payı var.
Yanılgı tarafı: "zaman yok" hesabı yalnızca yazma süresini sayar, doğrulama ve düzeltme süresini saymaz. Testsiz gitmek doğrulamayı ortadan kaldırmaz; onu elle, her seferinde, giderek daha eksik yapılan bir işleme çevirir. Değişiklik korkusu büyür, hata ayıklama seansları uzar, üretim hataları eğrinin pahalı ucundan tahsil edilir. Ekiplerin yaşadığı tipik döngü şudur: hız için test atlanır → hatalar artar → hataları kovalamak zaman yer → "hiç zamanımız yok" → daha çok atlanır. Yani testsizlik hız değil, taksitle ödenen yavaşlıktır — 11. derste bunun adı konacak: teknik borç.
Gerçeklik payı: test bedava değildir. Test de koddur; yazılır, okunur, bakımı yapılır. Kötü yazılmış test — yavaş, kırılgan, ne test ettiği belirsiz — negatif değer üretir: her refactor'da kırılır, ekip onu düzeltmekten yorulur ve sonunda ya siler ya görmezden gelir. "Her satıra test" bir erdem değildir; test yatırımı riske göre dağıtılır (10. bölüm). Doğru cevap "her zaman, her yerde, her şeye test" değil; "uzun yaşayacak ve değişecek kodda, davranışa odaklanmış, hızlı ve güvenilir test"tir.
Kıdemli duruş şudur: test yazma kararını pazarlık konusu bir lüks gibi değil, tahmine dahil bir maliyet gibi sunarsın. "Bu iş testleriyle üç gün" dersin; "iki gün, ama testsiz" seçeneğini açıkça riskiyle birlikte masaya koyarsın. Testi görünmez kılan ekipler, onu ilk sıkışıklıkta sessizce kaybeder.
06Manuel ↔ otomatik test
Otomasyon manuel testi tümüyle gereksiz kılmaz; ikisinin işi farklıdır.
| Otomatik test | Manuel / keşif testi | |
|---|---|---|
| Neyi iyi yapar | Bilinen beklentileri her seferinde aynı titizlikle, hızla ve usanmadan doğrulamak (regresyon) | Bilinmeyeni aramak: tuhaf akışlar, beklenmedik kombinasyonlar, "burada bir gariplik var" sezgisi, kullanılabilirlik |
| Tekrarlanabilirlik | Tam — aynı girdi, aynı doğrulama | Düşük — aynı kişi bile ikinci turda farklı bakar |
| Marjinal maliyet | Sıfıra yakın; yazma maliyeti baştadır | Her turda aynı; büyüdükçe katlanır |
| Zayıf yanı | Sorduğundan fazlasını göremez | Yorucu, eksik bırakır, insan hatasına açık |
Kural basittir: bir doğrulamayı ikinci kez elle yapıyorsan, otomatikleştirilecek bir aday bulmuşsundur. İnsan zekâsını tekrar işine harcamak israftır; makinenin göremeyeceği şeyi aramaksa (keşif testi, exploratory testing) insan işidir ve otomasyonla ikame edilemez. Olgun ekiplerde manuel emek regresyon koşusundan çekilir, keşfe ve yeni özelliğin ilk incelemesine kaydırılır.
07Test edilebilirlik bir tasarım özelliğidir
Şu metodu test etmeye çalış:
public class BasvuruServisi
{
public decimal GecikmeZammiHesapla(int basvuruId)
{
using var db = new SqlConnection("Server=uretim-db;..."); // gizli bağımlılık
var basvuru = db.QuerySingle<Basvuru>("SELECT ...", new { basvuruId });
var gecikenGun = (DateTime.Now - basvuru.SonOdemeTarihi).Days; // saate bağlı
var zam = basvuru.AnaPara * 0.001m * gecikenGun;
SmtpPosta.Gonder(basvuru.Eposta, $"Zam: {zam}"); // statik yan etki
return zam;
}
}
Test edemezsin — daha doğrusu, ancak gerçek veritabanı, gerçek saat ve gerçek e-posta sunucusuyla "test" edebilirsin, o da her gün farklı sonuç verir. Üç ayrı hastalık var: bağımlılık gizli (bağlantıyı metodun içi kuruyor), davranış zamana bağlı (DateTime.Now her çağrıda değişir) ve hesap ile yan etki iç içe (hesaplarken e-posta atıyor). Aynı mantık, sınıfın bağımlılıklarını dışarıdan aldığı ve saf hesabın ayrıştırıldığı hâlde:
public class GecikmeZammi
{
private readonly decimal gunlukOran;
public GecikmeZammi(decimal gunlukOran) => this.gunlukOran = gunlukOran;
public decimal Hesapla(decimal anaPara, int gecikenGun) =>
anaPara * gunlukOran * gecikenGun; // saf: girdi → çıktı, yan etki yok
}
public class BasvuruServisi
{
private readonly IBasvuruDeposu depo; // bağımlılıklar dışarıdan (Hat 5, DI)
private readonly IPostaServisi posta;
private readonly TimeProvider saat; // saat bile bir bağımlılıktır
// ... ctor enjeksiyonu; akış: oku → hesapla → bildir
}
Buradaki her karar Hat 4-5'ten tanıdık: bağımlılık enjeksiyonu, tek sorumluluk, saf fonksiyonun kenara ayrılması. Test bunların sınav kâğıdıdır: test etmesi zor olan kod, neredeyse her zaman kötü tasarlanmış koddur. Çok bağımlılığı olan sınıfın testi çok kurulum ister; gizli statik durum kullanan sınıfın testleri birbirini etkiler; her şeyi yapan metodun testi her şeyi ayağa kaldırır. Bu sinyali erken almak, TDD'nin (5. ders) "tasarım baskısı" dediği şeydir. Mimarideki adı da vardır: bağımlılığı kesip yerine sahtesini koyabildiğin her nokta bir dikiş yeridir (seam); dikişsiz kumaş sökmeden onarılamaz.
08Kırılgan test: implementasyona bağlanmak
Testin sözleşmesi şudur: davranış değişmedikçe test kırılmamalı; davranış bozulduğunda mutlaka kırılmalı. İki yönde de bozulabilir. İkincisi bariz kötüdür (hata kaçıran test); birincisi sinsidir: davranış aynı kaldığı hâlde, kodun iç yapısı değişti diye kırılan teste kırılgan test denir.
Kırılganlığın ana kaynağı, testin "ne yapıyor" yerine "nasıl yapıyor"a bağlanmasıdır: private metodun yansımayla test edilmesi, iç çağrı sırasının doğrulanması, aşırı mock'lamayla her iç etkileşimin beklentiye bağlanması (4. derste ayrıntısı), ekran çıktısının karakteri karakterine karşılaştırılması. Böyle bir paketle refactoring imkânsızlaşır: davranışı koruyarak yapıyı değiştirmek testleri kırar, ekip "testler yüzünden dokunamıyoruz" der — güven ağı kafese dönüşür.
Panzehir iki kuraldır: gözlemlenebilir davranışı test et (public API'den giren çıktı, kalıcı duruma etkisi, dışarıya giden çağrılar) ve testi de üretim kodu kadar özenli yaz — anlamlı isim, tekrarsız kurulum, tek net iddia. "Private metodu nasıl test ederim?" sorusunun kıdemli cevabı da budur: etmezsin; private metot public davranış üzerinden zaten test edilir, edilemiyorsa o metot muhtemelen ayrı bir sınıfın public işi olmak istiyordur.
09Kapsama yüzdesi ölçüt değildir
Kod kapsaması (code coverage), testler koşarken üretim kodunun hangi satırlarının çalıştığının ölçümüdür (.NET'te Coverlet toplar, 10. derste araçlara gireceğiz). Yararlı bir haritadır: hiç test edilmeyen bölgeleri gösterir. Sorun, haritanın hedefe çevrilmesiyle başlar: "kapsama %90'ın altına düşmesin" kuralı konduğu anda Goodhart yasası işler — ölçüt hedefe dönüşünce ölçüt olmaktan çıkar.
Neden? Çünkü kapsama yalnızca "bu satır çalıştı" der; doğrulandı demez. Assert içermeyen bir test kapsamayı doldurur ve hiçbir şey kanıtlamaz. Ayrıca ölçümün türü de yanıltır:
public string Degerlendir(int puan, bool ekKontenjan)
{
if (puan >= 70 || (ekKontenjan && puan >= 60))
return "Mülakata çağrılır";
return "Elenir";
}
Degerlendir(75, false) ve Degerlendir(50, false) ile
satır kapsaması %100 olur — ama ekKontenjan && puan >= 60 kolu
hiç çalışmadı; oradaki bir hata (mesela || yerine &&
yazılması) görünmezdir. Dal kapsaması (branch coverage) her koşul kolunu ayrı sayar
ve bu yüzden satır kapsamasından daha dürüsttür; yine de assert kalitesini o da ölçmez.
Assert kalitesini ölçen tek otomatik yöntem mutasyon testidir (.NET'te Stryker):
araç, üretim kodunda küçük bozulmalar (mutantlar) yapar — >= yerine
>, + yerine - — ve testlerin kaçını yakaladığına
bakar. Yakalanmayan mutant, "kapsanan ama doğrulanmayan" kodun kanıtıdır.
Kapsamanın doğru kullanımı: mutlak yüzde hedefi değil, eğilim ve boşluk analizi. "Kritik hesap modülünde hiç test edilmemiş dal var mı?" iyi bir sorudur; "%87 mi %90 mı" kötü bir sorudur. Pratik bir orta yol, tüm tabana tek sayı dayatmak yerine değişen satırların kapsamasına (diff coverage) bakmaktır: eski koda dokunmadan yeni kodun testli gelmesini ister — CI'daki kalite kapısı hâlini 18. hatta göreceksin.
10Hangi kod test edilir, hangisi edilmez
"Her şeyi test et" pratik bir kural değildir; test yatırımı da bir portföydür ve riske göre dağıtılır. İki eksen kullan: mantık yoğunluğu (karar, hesap, dönüşüm ne kadar var) ve değişme/hata sıklığı.
| Kod türü | Test kararı |
|---|---|
| İş kuralları, hesaplar, dönüşümler, ayrıştırma | Öncelikli. Mantık yoğun, hatası pahalı; birim testin ana bölgesi |
| Hata geçmişi olan, sık değişen modüller | Öncelikli. Hata kümelenir; bir kez kırılan yer yine kırılır |
| Salt veri sınıfları (POCO/DTO), otomatik property'ler | Test edilmez; doğrulanacak davranış yok |
| Üretilen kod (EF migration, kaynak üreteci çıktısı) | Test edilmez; üreticisine güvenilir, davranışı kullanan kod test edilir |
| Üçüncü parti kütüphane / framework'ün kendisi | Test edilmez — ama ona senin bağlanma noktan (sarmalayıcı, yapılandırma, sorgu) entegrasyon testiyle doğrulanır (4. ve 6. ders) |
| Mantıksız geçiş kodu (bir çağrıyı aynen ileten controller satırı) | Birim test değeri düşük; uçtan davranış entegrasyon testinde zaten kapsanır |
| Prototip / spike / atılacak kod | Test edilmez — ama "atılacak" kod üretime sızdığı anda bu karar geçersizleşir ve borç doğar |
Kıdem notu: bu tablo "test etmemek serbesttir" demez; kararın bilinçli ve söylenebilir olmasını ister. "Şu modülü test etmiyoruz çünkü salt veri taşıyor" savunulabilir bir cümledir; "test etmiyoruz çünkü hiç düşünmedik" değildir.
11Mini alıştırma — kendi kodunun test haritası
Yaklaşık 15 dakika · Kâğıt + kendi projen (yoksa Hat 5-6'da yazdığın API)
- Envanter. Projendeki sınıfları üç kolona ayır: mantık yoğun / geçiş kodu / salt veri. Hangi kolonda kaç sınıf var?
- Risk işaretle. Mantık yoğun kolondan, bozulursa en pahalıya patlayacak üç davranışı seç ve her biri için tek cümlelik beklenti yaz ("X girilirse Y dönmeli"). Bunlar ilk testlerinin listesi.
- Test edilebilirlik muayenesi. Bu üç davranışın kodunda şu üç hastalıktan
hangileri var: metot içinde
new'lenen bağımlılık,DateTime.Now/Guid.NewGuid()/Randomkullanımı, hesapla iç içe yan etki (e-posta, dosya, log dışı yazma)? Her bulguya bir satır: hangi bağımlılık dışarı alınmalı? - Kapsama provası. İçinde
&&ya da||geçen bir koşul bul. Satır kapsamasını %100 yapan ama bir dalı hiç çalıştırmayan iki çağrı yaz — 9. bölümdeki oyunu kendi kodunda göster. - Ekonomi cümlesi. Projendeki en sık elle doğruladığın senaryoyu yaz ve hesapla: elle bir tur kaç dakika, ayda kaç kez yapıyorsun? Bu çarpım, o senaryonun otomasyon bütçesidir.
12Kendini sına
Otomatik test nedir, neyi garanti eder, neyi edemez? "Testler geçiyor, demek ki hata yok" cümlesinin yanlışı nerededir?
Otomatik test, koddan beklenen davranışın kodla yazılmış, tekrarlanabilir doğrulamasıdır: belirli girdiler verilir, çıktı ya da etki beklentiyle karşılaştırılır. Garanti ettiği şey, sorulan soruların her çalıştırmada aynı titizlikle yeniden sorulmasıdır — regresyonu yakalayan budur. Garanti edemediği şey hatasızlıktır: test hatanın varlığını gösterir, yokluğunu kanıtlamaz (Dijkstra). Paket yalnızca yazarının düşündüğü senaryoları içerir; düşünülmemiş kenar durum, sorulmamış soru, assert'i zayıf yazılmış doğrulama paketten sessizce geçer. "Testler geçiyor"un doğru okunuşu "bildiğimiz beklentiler hâlâ tutuyor"dur; "hata yok" değil. Bu yüzden test paketi statik analiz, kod incelemesi ve üretim gözlemiyle (14-10) birlikte bir kanıt katmanıdır, tek başına bir kalite sertifikası değildir.
Regresyon nedir? Test paketinin "güven ağı" olması ne demektir ve bunun refactoring ile ilişkisi nedir?
Regresyon, önceden doğru çalışan bir davranışın yeni bir değişiklikle bozulmasıdır; hataların önemli bölümü yeni kodda değil, değişikliğin dokunmadığını sandığı komşu davranışlarda çıkar. Test paketi bu riske karşı güven ağıdır: her değişiklikten sonra tüm bilinen beklentiler saniyeler içinde yeniden doğrulanır, bozulan varsa yeri ve beklentisiyle görünür. Bu ağ olmadan ekip değişiklikten korkar, korkan ekip dokunmaz, dokunulmayan kod çürür — Feathers'ın "miras kod, testi olmayan koddur" tanımının nedeni budur. Refactoring tanım gereği "davranışı koruyarak yapıyı değiştirmek" olduğundan, davranışın korunduğunu kanıtlayan bir mekanizma olmadan refactoring yapılamaz; test paketi o mekanizmadır. Ağın işlemesi için testlerin davranışa bağlanması şarttır: iç yapıya bağlanan kırılgan testler tam tersine refactoring'i engeller.
"Test yazacak zaman yok" argümanına nasıl cevap verirsin? Hangi durumda test yazmamak savunulabilir bir karardır?
Önce hesabın eksiğini gösteririm: "zaman yok" yalnızca yazma süresini sayar; doğrulama ve düzeltme süresini saymaz. Testsizlik doğrulamayı yok etmez, elle ve her seferinde tekrarlanan bir işe çevirir; hatalar kusur maliyeti eğrisinin pahalı ucunda (üretimde) yakalanır ve oradan tahsil edilir — bozulan verinin onarımı da faturaya eklenir. Tipik döngü, hız için atlanan testin ürettiği hataların zamanı yemesi ve "daha da zaman yok" sarmalıdır; yani testsizlik hız değil taksitle ödenen yavaşlıktır. Öte yandan argümanın gerçek payını da teslim ederim: test de koddur, bakım maliyeti vardır ve kötü test (yavaş, kırılgan, ne doğruladığı belirsiz) negatif değer üretir. Test yazmamak şu durumlarda savunulabilir: gerçekten atılacak keşif kodu (spike/prototip), doğrulanacak davranışı olmayan salt veri sınıfları, üretilen kod ve üçüncü parti kütüphanenin kendisi. Kritik şart, kararın bilinçli olması ve "atılacak" kodun üretime sızdığı anda kararın yeniden verilmesidir. Kıdemli pratik, testi pazarlık edilir bir ek değil tahminin parçası olarak sunmaktır.
Kapsama yüzdesi neden tek başına kalite ölçütü değildir? Satır ↔ dal kapsaması farkı nedir, mutasyon testi neyi ölçer?
Kapsama, testler koşarken hangi kodun çalıştığını ölçer;
çıktının doğrulandığını ölçmez — assert içermeyen bir test kapsamayı doldurur ama
hiçbir hatayı yakalayamaz. Yüzde hedefe dönüştürüldüğünde Goodhart yasası işler: ekip
sayıyı yükseltmenin en ucuz yolunu bulur (kolay ama değersiz testler) ve ölçüt anlamını
yitirir. Satır kapsaması bir satır herhangi bir yoldan çalıştıysa sayar;
if (a || b) tek testle "kapsanmış" görünür ama b kolu hiç
denenmemiştir. Dal kapsaması her koşul kolunu ayrı saydığı için daha dürüsttür; yine de
doğrulama kalitesini o da göremez. Bunu ölçen mutasyon testidir: araç (Stryker.NET) üretim
kodunda küçük bozulmalar yapar — işleç değiştirir, sabit oynatır — ve test paketinin kaç
mutantı öldürdüğüne bakar; sağ kalan mutant, çalıştırılan ama doğrulanmayan kodu kanıtlar.
Kapsamanın doğru kullanımı mutlak hedef değil boşluk haritasıdır: kritik modülde test
edilmemiş dallar ve değişen satırların testli gelip gelmediği (diff coverage).
13Sınav köşesi
Yazılıda nasıl sorulur
(1) Yazılım testi nedir, amacı nedir? (2) Regresyon nedir? (3) Yazılım hatalarının erken tespit edilmesi neden önemlidir? (4) Manuel test ile otomatik test arasındaki farklar nelerdir? (5) Birim test nedir, ne işe yarar? (6) Kod kapsaması (coverage) nedir? (7) Test edilebilirlik nedir, hangi tasarım ilkeleriyle ilişkilidir? (8) "Test hatanın yokluğunu kanıtlamaz" sözünü açıklayınız. (9) Keşif (exploratory) testi nedir? (10) Test yazmanın maliyeti ve faydası nasıl değerlendirilir?
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Tanım — "Test, koddan beklediğim davranışı otomatik doğrulamaya çevirmektir; asıl değeri regresyondadır: her değişiklikte tüm bilinen beklentiler saniyeler içinde yeniden doğrulanır, bu da değişiklik yapma cesareti verir." (2) Ekonomi — "Hata ne kadar geç bulunursa o kadar pahalı; test kusuru eğrinin ucuz ucunda, yazarken yakalar. 'Test yazacak zaman yok' hesabı doğrulama ve düzeltme süresini saymadığı için eksiktir." (3) Tasarım bağı — "Test edilebilirlik bir tasarım özelliğidir: bağımlılıkları dışarıdan alan, saf hesabı yan etkiden ayıran kod kolay test edilir; test etmesi zor kod bana tasarım hatasını erken söyler." (4) Ölçüm — "Kapsama yüzdesini hedef değil harita olarak kullanırım: assert'süz test de kapsama üretir; dal kapsamasına ve gerekirse mutasyon testine bakarım." (5) Kıdem vuruşu — "Testi davranışa yazarım, iç yapıya değil — refactor'da kırılan test yanlış testtir. Test yatırımını da riske göre dağıtırım: iş kuralları ve hata geçmişi olan modüller önce, salt veri sınıfları hiç; ama bu kararı bilinçli veririm ve söyleyebilirim."
