TB
← Tüm yazılar

Mutasyon testi girişi

Mutasyon testinin temel kavramlarını, mutant operatörlerini ve mutasyon skorunun test kalitesini nasıl ölçtüğünü uygulamalı örneklerle açıklıyoruz.

Kod kapsaması %95 görünür; tüm satırlar test tarafından çalıştırılmıştır. Ancak assertion zayıfsa testler anlamsızdır. Mutasyon testi, kaynak koda kasıtlı küçük değişiklikler (mutant) enjekte ederek test süitinin bu değişiklikleri yakalayıp yakalayamadığını ölçer. Test kalitesinin röntgenini çeker.

Temel kavramlar

Mutasyon testi üç kavram etrafında döner: mutant, mutant operator ve mutasyon skoru. Mutant, orijinal koddan türetilen değiştirilmiş versiyondur. Operator, değişikliğin türünü belirler. Skor, öldürülen mutant oranıdır.

  • Mutant: kaynak kodda tek bir değişiklik yapılmış kopya
  • Killed mutant: test suite en az bir testi fail etti
  • Survived mutant: tüm testler geçti; test açığı var
  • Timeout mutant: sonsuz döngü; test zaman aşımına uğradı
  • Equivalent mutant: davranış değişmeyen mutant; sayılmaz

Mutant operatorleri

Operatorler dil ve araç tarafından tanımlanır. Yaygın operatorler aritmetik, karşılaştırma ve koşul mutasyonlarını kapsar.

Aritmetik operator mutasyonu

+ yerine -, * yerine / konur. Finansal hesaplama modülünde toplama hatası test tarafından yakalanmazsa survived mutant oluşur.

Karşılaştırma mutasyonu

> yerine >=, == yerine != değiştirilir. Sınır değer testlerinin eksikliği bu mutantlarla ortaya çıkar.

Koşul mutasyonu

If koşulu true/false sabitine çevrilir veya koşul tamamen kaldırılır. Yetkilendirme kontrolünün test edilip edilmediği bu operatorle doğrulanır.

Return value mutasyonu

Return değeri null, zero veya sabit bir değere çevrilir. Null reference koruması test edilmemişse mutant hayatta kalır.

// Orijinal
if (amount > 0) return amount * taxRate;
return 0;

// Mutant 1: > yerine >=
if (amount >= 0) return amount * taxRate;

// Mutant 2: koşul true
if (true) return amount * taxRate;

Mutasyon skoru

Mutasyon skoru = (öldürülen mutant / toplam geçerli mutant) × 100. %100 skor nadirdir; equivalent mutant ve pratik sınırlar vardır. Skor %60'ın altındaysa test suite güvenilir değildir; %80+ iyi kabul edilir. Skor kapsama metriğinden daha güvenilir kalite göstergesidir.

Stryker.NET ile uygulama

.NET ekosisteminde Stryker.NET en olgun mutasyon test aracıdır. Proje kökünde dotnet stryker komutu çalıştırılır.

dotnet stryker --project OrderService.csproj \
  --mutate OrderService/Domain/**/*.cs \
  --threshold-high 80 \
  --threshold-low 60 \
  --break-at 60

HTML raporunda survived mutant'lar dosya ve satır numarasıyla listelenir. Geliştirici ilgili satıra gidip eksik assertion'ı ekler. Rapor PR yorumuna eklenerek review sürecine dahil edilir.

Performans ve kapsam

Mutasyon testi yavaştır; her mutant için tüm test suite çalışır. Pratik uygulamada yalnızca değişen dosyalar mutate edilir (diff scope). Nightly pipeline tam mutasyon, PR pipeline diff mutasyon çalıştırır.

Hızlandırma teknikleri

  • Incremental mutation: yalnızca değişen satırlardaki mutantlar
  • Parallel execution: çok çekirdekli runner
  • Test subset: mutant konumuna göre ilgili testler filtrelenir
  • Coverage guided: zaten kapsanmayan satırda mutant üretilmez

Equivalent mutant problemi

Bazı mutantlar program davranışını değiştirmez; test fail etmemesi normaldir. Örnek: x + 0 yerine x - 0. Equivalent mutant tespiti otomatik tam çözülemez; manuel review gerekebilir. Skor hesabından çıkarılır.

Mutasyon vs kapsama

Kapsama "kod çalıştı mı" der; mutasyon "test hata yakaladı mı" der. İkisi birlikte kullanıldığında sahte güven ortadan kalkar. Yüksek kapsama + düşük mutasyon skoru = assertion'sız testler.

Hangi kod mutate edilmeli?

Tüm codebase mutate edilmez. Domain logic, finansal hesaplama, yetkilendirme ve business rule modülleri önceliklidir. UI markup, configuration boilerplate ve generated code hariç tutulur.

CI entegrasyonu

PR'da diff mutasyon skoru eşiğin altındaysa uyarı veya hard gate uygulanır. Nightly tam mutasyon raporu trend grafiği üretir. Skor düşüş trendi test kalitesi erozyonuna işaret eder.

Ekip adoption

Mutasyon testi ilk tanıtıldığında geliştiriciler survived mutant listesine şaşırır. Bu eğitici etki değerlidir. Pair session ile ilk survived mutant'lar birlikte kapatılır; ekip hızla anlamlı assertion yazmayı öğrenir.

Gerçek örnek

DiscountService'te if (customer.IsPremium) discount = 0.15 satırı %100 satır kapsamasına sahiptir. Mutasyon testi koşulu false yapar; hiçbir test fail olmaz. Eksik test: premium olmayan müşterinin indirim almadığı doğrulanmamış. Yeni test eklenir, mutant öldürülür.

Dil ve platform desteği

Mutasyon testi JavaScript, Java, C#, Python, Go dillerinde desteklenir. Frontend component testlerinde Stryker JS; backend domain logic'te Stryker.NET kullanılır. Mobile (Kotlin/Swift) mutasyon testi daha az olgundur; kritik hesaplama shared module üzerinde cross-platform test edilir.

Test yazımında mutasyon odaklı düşünme

Mutasyon testi mindset'i geliştiriciye şu soruyu sorar: "Bu satır değişse test fail olur mu?" Test yazarken her assertion bilinçli olmalıdır. Arrange-Act-Assert yapısında Assert adımı mutasyon dirençliliğini belirler. Zayıf assert (yalnızca null check) çoğu mutant'a karşı kör kalır.

Assertion kalitesi örnekleri

  • Zayıf: Assert.NotNull(result) — tip değişimini yakalamaz
  • Güçlü: Assert.Equal(150.00m, result.Total) — aritmetik mutant öldürür
  • Zayıf: Assert.True(response.IsSuccess) — status code mutant geçer
  • Güçlü: Assert.Equal(HttpStatusCode.Created, response.StatusCode)

Mutasyon testi kontrol listesi

  1. Domain modülleri mutate scope'a dahil mi?
  2. Nightly full mutation raporu saklanıyor mu?
  3. Survived mutant backlog'a ticket açılıyor mu?
  4. Equivalent mutant manuel review süreci var mı?
  5. PR diff mutation eşiği tanımlı mı?

Mutasyon testi rapor yorumlama

Survived mutant listesi öncelik sırasına göre ele alınır: finansal hesaplama, yetkilendirme, veri bütünlüğü modülleri önce. Her survived mutant için eksik test senaryosu backlog'a eklenir. Mutasyon raporu sprint review'da kapsama raporu ile yan yana incelenir.

Diğer mutasyon araçları

JavaScript ekosisteminde Stryker JS, Java'da PIT (PITest) yaygındır. Python'da mutmut ve cosmic-ray kullanılır. Araç fark etmeksizin operator seti ve skor yorumu benzerdir. Çok dilli monorepo'da her dil için ayrı mutasyon job'ı nightly pipeline'da çalışır.

Mutasyon testi, test suite'in gerçekten ne kadar güvenilir olduğunu sayısal olarak gösteren ileri seviye bir kalite aracıdır. Kapsama metriklerini tamamlar; birlikte kullanıldığında refactor güveni ve teslimat hızı artar.

Mutasyon ve refactor güveni

Yüksek mutasyon skorlu modülde refactor korkusuz yapılır. Geliştirici davranış değiştirir; test suite anında geri bildirim verir. Düşük skorlu modülde refactor ertelenir; önce test kalitesi artırılır. Bu disiplin teknik borç spiralini kırar.

Mutasyon testi eğitim değeri

Junior geliştiriciler survived mutant listesiyle test yazmayı öğrenir. Pair programming oturumunda mutant öldürme egzersizi yapılır: kasıtlı kod değişikliği, test fail etmeli. Bu pratik assertion kalitesini hızla yükseltir.

Production mutant simülasyonu

Chaos engineering mantığına benzer şekilde staging ortamında kontrollü fault injection yapılabilir. Mutasyon testi bu riski daha erken, CI ortamında yakalar. İkisi birlikte dayanıklılık ve test kalitesi katmanı oluşturur.

Mutasyon skoru hedefleri

Domain katmanı minimum %80 mutasyon skoru hedefler. Infrastructure katmanı %60 ile yetinir. Hedefler coverage eşikleriyle aynı YAML dosyasında versiyonlanır. Sprint review'da survived mutant sayısı bir önceki sprint ile karşılaştırılır.

Kapsam dışı bırakma kuralları

DTO, migration, Program.cs ve auto-generated mapper dosyaları mutasyon scope dışındadır. Exclude listesi code review ile onaylanır; kolay yol olarak domain modülü hariç tutulamaz. Exclude oranı %15'i geçmemelidir. Her exclude gerekçesi PR açıklamasında belirtilir.

Özet

Mutasyon testi coverage metriğinin ötesinde test suite güvenilirliğini ölçer. Domain modüllerinde düzenli uygulandığında assertion kalitesi ve refactor cesareti belirgin şekilde artar.