İçeriğe geç

Kapsama ve Mutasyon — Yüzde Yüz Test, Hata Nasıl Geçti?

Orta 8 dk Sık karşılaşılır

Önce şunu oku: TDD — Red, Green, Refactor

30 saniyede özet

Kapsama oranı bir satırın test sırasında çalıştığını söyler, doğru sonuç verdiğini değil. Mutasyon testi kodda bilerek küçük hatalar yapar ve testlerin onları yakalayıp yakalamadığına bakar; yaşayan değişiklik eksik kontrolü gösterir.

Bir yangın denetçisi evin bütün odalarını geziyor ve rapora “yüzde yüz kontrol edildi” yazıyor. Ama hiçbir alarmı denemiyor. Asıl denetim, her odada bilerek biraz duman çıkarıp hangi alarmın sessiz kaldığına bakmaktır.

  1. Bayt: Havale ücreti fonksiyonunun kapsaması yüzde yüz. Testlerimiz sağlam olmalı.

  2. Sen: Canlıda tam 10.000 TL'lik havalelerden yanlış ücret alınmış. Hiçbir test kırmızı olmadı.

  3. Bayt: Testlere baktım: fonksiyonu çağırıyorlar ama sonucu hiç kontrol etmiyorlar!

  4. Bayt: Odaları gezmek yetmiyor; her alarmı tek tek denememiz gerekiyor.

Kapsama neyi ölçer?

kapsama oranıTestler çalışırken kodun satırlarının ne kadarının en az bir kez çalıştığı. Satırların doğru sonuç verdiğini ölçmez.Sözlükte gör →, testler çalışırken kodun satırlarının ne kadarının en az bir kez çalıştığını söyler. Düşük kapsama işe yarar bir uyarıdır: orada hiç test yoktur.

Ama yüksek kapsama bir güvence değildir. Fonksiyonu çağıran ve hiçbir sonucu kontrol etmeyen bir test de her satırı çalıştırır.

Kafam karıştı, daha basit anlat

Kapsama “bu satır çalıştı” der, “doğru çalıştı” demez.

Hızlı kontrolBaşlangıç

Satır kapsaması (code coverage) neyi ölçer?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Hiçbir şeyi kontrol etmeyen ama her satırı çağıran testlerin kapsaması nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bilerek hata koymak

Testler 500 TL için ücretin 5 TL, 20.000 TL için 20 TL olduğunu kontrol ediyor. Biri koddaki >= 10_000 koşulunu > 10_000 yaptı. Testler ne der? Cevabı göster

Yeşil kalırlar. 500 TL ve 20.000 TL için sonuç değişmedi. Fark yalnızca tam 10.000 TL’de ortaya çıkar ve o değer hiç denenmedi.

Gezmek değil, alarmın çalması önemli.
Adım adım oku
  1. Denetçi her odaya girdi: yüzde yüz gezildi.
  2. Ama hiçbir alarmı denemedi.
  3. Her odada bilerek biraz duman çıkar.
  4. Gezmek değil, alarmın çalması önemli.

mutasyon testiKodu kasten bozup testlerin fark edip etmediğini ölçer. Coverage'ın cevaplayamadığı asıl soruyu sorar: bu testler gerçekten bir şey koruyor mu?Sözlükte gör → tam bunu yapar: kodda küçük bir değişiklik yapar, testleri çalıştırır ve kırmızı olan bir test var mı diye bakar. Her değişiklik bir koşulu çevirmek, bir sayıyı değiştirmek ya da bir satırı silmek olabilir.

Hiçbir testin fark etmediği değişikliğe yaşayan mutantKodda yapılan küçük bir değişikliğe rağmen hiçbir testin kırmızıya dönmemesi. O davranışı kontrol eden test eksiktir.Sözlükte gör → denir. Her biri, o davranışı kontrol eden bir testin eksik olduğunu gösterir.

Kafam karıştı, daha basit anlat

Kodu bilerek boz, testler fark ediyor mu bak. Fark etmiyorsa orada bir test eksik.

Hızlı kontrolOrta

Mutasyon testi nasıl çalışır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Bir mutant 'yaşadı' (survived) ne demektir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Havale ücreti fonksiyonu ve dört küçük değişiklik

Tohum 749379

Satır kapsaması: %100

  1. >= 10_000 yerine > 10_000
  2. return 5 yerine return 0
  3. amount <= 0 kontrolü silindi
  4. 0.001 yerine 0.01

Yakalanan değişiklik: 0/4

Hız
Adım 0

Şu an ne oldu?

Çağırır ama hiçbir şeyi kontrol etmez · kapsama %100

Araç kodda küçük bir değişiklik yapıp testleri çalıştırıyor.

Görevler0/3

  • Kapsama %100 olsun, hiçbir değişiklik yakalanmasınaçık

    İpucu

    Kontrol etmeyen testler.

  • Sınır değişikliği fark edilmeden geçsinaçık

    İpucu

    Yalnızca normal tutarlar.

  • Dört değişikliğin dördü de yakalansınaçık

    İpucu

    Sınır ve hatalı girdi.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. Testler hiçbir şeyi kontrol etmiyor: kapsama yüzde yüz, dört değişikliğin hiçbiri yakalanmadı.
  2. “Yalnızca normal tutarlar” seç. Kapsama yüzde seksene düştü, ama iki değişiklik yakalandı. Sınır ve hatalı girdi yine geçti.
  3. “Sınır ve hatalı girdi de” seç. Dördü de yakalandı.
Hızlı kontrolOrta

>= 10000 koşulu > 10000 yapıldığında testler yeşil kaldı. Eksik test hangisidir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kapsamanın düşük olduğu bir yer neyi kesin söyler?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Kapsamayı hedef yapmak. “Kapsama yüzde doksanın altına düşemez” kuralı, hiçbir şey kontrol etmeyen testlere yol açabilir. Kapsamayı test eksikliğini bulmak için kullan, başarı ölçüsü olarak değil.

Her commit’te bütün kodu mutasyona sokmak. Her değişiklik için testler yeniden çalıştığından pahalıdır. Değişen dosyalarla sınırla ya da gece çalıştır.

Eşdeğer mutantlar. Bazı değişiklikler davranışı hiç değiştirmez; hiçbir test onları yakalayamaz. Raporda bunları gözle ele.

Kafam karıştı, daha basit anlat

Kapsamayı hedef yapma, mutasyonu sınırlı çalıştır, davranışı değiştirmeyen değişiklikleri ele.

Hızlı kontrolOrta

Kapsama oranı için hedef koymanın riski nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
Derinleş · Havale ücreti: sınırı ve hatalı girdiyi test etmek 4 dosya · ~71 satır · ilk okumada atlayabilirsin
Proje dosyaları

payments/src/main/java/com/bank/payments/ TransferFee.java Fonksiyon: 10.000 TL ve üstüne binde bir, altına sabit 5 TL; sıfır ve altı reddedilir.

payments/src/main/java/com/bank/payments/TransferFee.java
public final class TransferFee {
private static final BigDecimal LARGE = new BigDecimal("10000");
private static final BigDecimal RATE = new BigDecimal("0.001");
private static final BigDecimal FLAT = new BigDecimal("5");
private TransferFee() {
}
public static BigDecimal of(BigDecimal amount) {
if (amount.signum() <= 0) {
throw new IllegalArgumentException("Tutar sıfırdan büyük olmalı.");
}
if (amount.compareTo(LARGE) >= 0) {
return amount.multiply(RATE).setScale(2, RoundingMode.HALF_EVEN);
}
return FLAT;
}
}

payments/src/test/java/com/bank/payments/ TransferFeeTest.java Testler: normal tutarlar, tam sınır ve hatalı girdi ayrı ayrı kontrol ediliyor.

payments/src/test/java/com/bank/payments/TransferFeeTest.java
class TransferFeeTest {
@Test
void a_small_transfer_pays_the_flat_fee() {
assertThat(TransferFee.of(new BigDecimal("500"))).isEqualByComparingTo("5");
}
@Test
void a_large_transfer_pays_one_per_mille() {
assertThat(TransferFee.of(new BigDecimal("20000"))).isEqualByComparingTo("20");
}
@Test
void exactly_ten_thousand_already_counts_as_large() {
// The boundary: kills the ">= becomes >" mutant.
assertThat(TransferFee.of(new BigDecimal("10000"))).isEqualByComparingTo("10");
}
@Test
void zero_and_negative_amounts_are_rejected() {
assertThatThrownBy(() -> TransferFee.of(BigDecimal.ZERO)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> TransferFee.of(new BigDecimal("-1"))).isInstanceOf(IllegalArgumentException.class);
}
}

payments/ pom.xml (plugin) PIT: değişen sınıflar için mutasyon testi; yaşayan mutantlar raporlanıyor.

payments/pom.xml (plugin)
<plugin>
<groupId>org.pitest</groupId>
<artifactId>pitest-maven</artifactId>
<configuration>
<targetClasses>
<param>com.bank.payments.*</param>
</targetClasses>
<!-- On pull requests, only mutate what changed. -->
<features>
<feature>+GIT(from[HEAD~1])</feature>
</features>
<mutationThreshold>80</mutationThreshold>
</configuration>
</plugin>

payments/src/test/java/com/bank/payments/ TransferFeeHollowTest.java Şöyle de yazılabilirdi, ama bak ne oluyor: her satırı çağıran ama hiçbir şey kontrol etmeyen test.

payments/src/test/java/com/bank/payments/TransferFeeHollowTest.java
// Tempting, but look what happens: every line runs, nothing is checked.
class TransferFeeHollowTest {
@Test
void runs_everything() {
TransferFee.of(new BigDecimal("500"));
TransferFee.of(new BigDecimal("20000"));
try {
TransferFee.of(new BigDecimal("-1"));
} catch (IllegalArgumentException ignored) {
// coverage: 100%, mutants killed: 0
}
}
}

Kendini sına

Şimşek turu1/4

Kapsama oranı yüzde yüzse testler her hatayı yakalar.

Soru 1/3İleri

Mutasyon testi neden her commit'te bütün kod için çalıştırılmaz?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Kapsama oranı satırların çalıştığını ölçer; sonucun kontrol edildiğini ölçmez. Hiçbir şeyi kontrol etmeyen testler de yüzde yüze ulaşabilir.
  2. 2 Mutasyon testi kodda küçük bir hata yapar ve testleri çalıştırır. Fark edilmeyen her değişiklik, o davranışı kontrol eden bir testin eksik olduğunu söyler.
  3. 3 Hatalar en çok sınırlarda saklanır: tam sınır değerini ve hatalı girdiyi ayrı test et.
Sonraki kapı common modülünde bir metodu değiştirdin, build yeşil yandı. Ertesi gün rapor uygulaması derlenmedi. Build neyi atladı? Çok Modüllü Maven — common'ı Değiştirdim, Neyi Derlemeliyim? · 9 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı