İçeriğe geç

TDD — Red, Green, Refactor

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

Önce şunu oku: Test Piramidi, JUnit 5 ve Mockito

30 saniyede özet

TDD'de testi koddan önce yazarsın ve önce kırmızı yandığını görürsün. Neden? Hiç kırmızı yanmamış bir test, gerçekten bir şeyi kontrol ettiğini hiç kanıtlamamıştır.

Bazı yazılımcıların tuhaf bir alışkanlığı var: kodu yazmadan önce onu sınayacak testi yazarlar. Sebebini bilmezsen bu bir ritüele dönüşür, o yüzden önce sebebe bakalım.

  1. Bayt: Kodu yazdım, ardından testini yazdım. İlk seferde yeşil yandı!

  2. Sen: Harika, demek ki kod doğru.

  3. Bayt: Ya da test hiçbir şeyi kontrol etmiyor. İkisini nasıl ayırt ederim?

  4. Bayt: Testi bir kez kırmızı görseydim bilirdim...

Kırmızı, yeşil, düzenle. Sonra yeniden.
Adım adım oku
  1. Önce test yazılır; kod henüz olmadığı için test düşer. Bu kırmızı, testin gerçekten bir şeyi kontrol ettiğinin kanıtıdır.
  2. Sonra testi geçirecek en basit kod yazılır ve test yeşile döner.
  3. Testler yeşilken kod düzenlenir; davranışın bozulmadığını testler söyler.
  4. Kod önce yazılırsa test ilk anda yeşildir ve hiç kırmızı görmediği için hiçbir şey kanıtlamaz.

Kırmızı adım neyi kanıtlar?

Yeni yazdığın bir test daha ilk çalıştırmada yeşil geçti. Bu, kodun doğru olduğunu gösterir mi? Cevabı göster

Hiçbir şey göstermez. Test kod doğru olduğu için de geçiyor olabilir, hiçbir şeyi kontrol etmediği için de; assertTrue(true) de geçer.

Testi önce kırmızı görmek, aradaki farkı gösteren tek kanıttır. Kırmızıdan yeşile geçişi izlediğinde, o testin gerçekten o davranışa bağlı olduğunu bilirsin.

Kafam karıştı, daha basit anlat

Hiç kırmızı yanmamış bir test, alarmı hiç çalmamış bir yangın dedektörü gibidir. Gerçekten çalıştığını bilmezsin. Kırmızı adım, dedektörün duman görünce öttüğünü gösterir.

Hızlı kontrolBaşlangıç

TDD'nin üç adımı nedir?

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

Kendin gör

Aşağıdaki kata kendi kendine doğru döngüyü işletiyor. Önce temiz bir tur izle, sonra kırmızı düğmelerle kısayolları dene ve Suite güveni çubuğuna ne olduğuna bak.

TDD döngüsü — Red, Green, Refactor

Tohum 2
RED GREEN REFACTOR

Test suite

  • fizzBuzz(1) == "1"
  • fizzBuzz(3) == "Fizz"
  • fizzBuzz(6) == "Fizz"
  • fizzBuzz(5) == "Buzz"
  • fizzBuzz(15) == "FizzBuzz"

Üretim kodu

// henüz kod yok
Suite güveni%100

Yazılmış testlerin kaçı kırmızı görülerek kanıtlandı.

Hız
Adım 0

Şu an ne oldu?

RED — sıradaki: fizzBuzz(1) == "1"

Önce başarısız olan bir test yazılır. Testi kırmızı görmek, o testin gerçekten bir şeyi kontrol ettiğinin tek kanıtıdır.

Aklında kalsın: "Neden önce test?" sorusunun cevabı tasarım değil, kanıttır: kırmızı görmediğin bir test, sessizce hep yeşil kalabilir.

Olay günlüğü (0)

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

Üç kısayol, üç farklı kayıp:

  • Önce kodu yaz — test yazıldığı anda yeşil. Suite dolu görünüyor ama o test hiçbir zaman bir hatayı yakalayacağını kanıtlamadı. En sinsi olanı bu: hiçbir alarm çalmaz.
  • Hepsini bir anda yaz — kalan bütün testler yazıldıkları anda yeşil oluyor. Suite güveni çöküyor. Coverage aracı bunu %100 gösterir.
  • Kırmızıyken refactor et — davranışı değiştirmediğini doğrulayacak hiçbir şey yok, çünkü suite zaten kırmızı.

Bir de üçgenleme adımına dikkat et: fizzBuzz(6) == "Fizz" testi yazıldığı anda geçiyor ve bu beklenen davranış. Simülatör bunu ihlal olarak saymıyor, çünkü amacı yeni davranış sürüklemek değil, mevcut genellemeyi doğrulamak.

Hızlı kontrolBaşlangıç

Testi önce **kırmızı** görmek neden önemli?

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

Üç adımın her birinin işi ayrı

AdımAmaçAtlanırsa
RedTestin gerçekten bir şeyi kontrol ettiğini kanıtlamakSessizce hep yeşil kalan testler
GreenTesti geçirecek en az kodu yazmakHiçbir testin sürüklemediği, gereksiz kod
RefactorDavranış sabitken yapıyı iyileştirmekTDD “önce test yaz”a iner, tasarım kazancı kaybolur

En çok atlanan adım refactor. Atlanınca geriye kalan şey test-first geliştirmedir; işe yarar ama TDD’nin tasarım faydasının tamamı o üçüncü adımdadır.

Green adımında “aptal” kod yazmak normaldir.

Adım 1 — GREEN
String fizzBuzz(int n) {
return "1"; // evet, gerçekten böyle yazılır
}

Bu kasıtlıdır. Genelleme, bir sonraki kırmızı testin zorlamasıyla gelir:

Adım 2 — yeni kırmızı test genellemeye zorluyor
assertEquals("2", fizzBuzz(2)); // "1" döndüren kod artık yetmiyor
String fizzBuzz(int n) {
return String.valueOf(n); // genelleme, tahminle değil baskıyla geldi
}

Buna üçgenleme (triangulation) denir. Fazladan yazdığın her satır, hiçbir testin talep etmediği koddur — ve hiçbir testin talep etmediği kod, hiçbir testin korumadığı koddur.

Aynı döngüyü bir bankanın havale ekranındaki IBAN kontrolünde izleyebilirsin. Testler yazıldıkları sırayla duruyor; sonuncusu bir müşterinin bildiriminden doğdu.

Derinleş · IBAN kontrolü: test test büyüyen kod 3 dosya · ~57 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/test/java/com/bank/iban/ IbanValidatorTest.java Testler yazıldıkları sırayla; her birinin yanında neyi kırmızıya çevirdiği yazıyor.

src/test/java/com/bank/iban/IbanValidatorTest.java
// Tests in the order they were written; each one was seen red before its code existed.
class IbanValidatorTest {
private final IbanValidator validator = new IbanValidator();
@Test // 1. Red: IbanValidator did not exist. Green: return true.
void acceptsAValidTurkishIban() {
assertThat(validator.isValid("TR330006100519786457841326")).isTrue();
}
@Test // 2. Red: "return true" accepts anything. Green: check the length.
void rejectsAWrongLength() {
assertThat(validator.isValid("TR3300061005197864578413")).isFalse();
}
@Test // 3. Red: a typo has the right length. Green: the mod-97 checksum.
void rejectsATypoInTheAccountNumber() {
assertThat(validator.isValid("TR330006100519786457841327")).isFalse();
}
@Test // 4. Triangulation: green at once, and that is expected.
void acceptsAnotherValidIban() {
assertThat(validator.isValid("TR360001000123456789012345")).isTrue();
}
}

src/test/java/com/bank/iban/ IbanWithSpacesTest.java Müşteri bildirimi önce kırmızı bir teste dönüştü, düzeltme ondan sonra geldi.

src/test/java/com/bank/iban/IbanWithSpacesTest.java
// Customer report: "My IBAN is rejected when I paste it from my bank's app."
// The app copies it in groups of four. This test came first and went red.
class IbanWithSpacesTest {
@Test
void acceptsAnIbanWrittenInGroupsOfFour() {
assertThat(new IbanValidator().isValid("TR33 0006 1005 1978 6457 8413 26")).isTrue();
}
}

src/main/java/com/bank/iban/ IbanValidator.java Refactor sonrası son hâli: her satırını bir test istedi.

src/main/java/com/bank/iban/IbanValidator.java
// Turkish IBANs: "TR", two check digits and 22 digits, verified with the ISO 13616 mod-97 check.
final class IbanValidator {
private static final Pattern TURKISH_IBAN = Pattern.compile("TR\\d{24}");
boolean isValid(String input) {
String iban = input.replace(" ", "");
return TURKISH_IBAN.matcher(iban).matches() && mod97(iban) == 1;
}
// Move the first four characters to the end, read letters as numbers (A=10 ... Z=35)
// and keep only the remainder, so the huge number is never built.
private static int mod97(String iban) {
String rearranged = iban.substring(4) + iban.substring(0, 4);
int remainder = 0;
for (char c : rearranged.toCharArray()) {
int value = Character.getNumericValue(c);
remainder = (value > 9 ? remainder * 100 : remainder * 10) + value;
remainder %= 97;
}
return remainder;
}
}
Kafam karıştı, daha basit anlat

Kırmızı: test başarısız olur. Yeşil: en basit kodla testi geçirirsin. Toplama: kodu düzenlersin ve testler yeşil kalır; bu adım atlanırsa dağınıklık birikir.

Hızlı kontrolOrta

Her eylemi TDD döngüsünde ait olduğu adıma yerleştir.

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

Sınıflandırılmamış

RED aşaması

Başarısız test yazılır

    GREEN aşaması

    Testi geçirecek en az kod

      REFACTOR aşaması

      Davranış sabit, yapı iyileşir

        Her ifadeyi, TDD'nin gerçekten verdiği bir şey mi yoksa ona yüklenen bir beklenti mi olduğuna göre ayır.

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

        Sınıflandırılmamış

        TDD'nin verdiği

        Döngünün doğrudan sonucu

          Yüklenen beklenti

          Doğru olabilir ama TDD garanti etmez

            Coverage yalan söyler, mutasyon testi söylemez

            Test coverageTestler sırasında çalıştırılan kod oranı. Kodun doğrulandığını değil, yalnızca çalıştırıldığını gösterir — assertion içermeyen bir test de coverage üretir.Sözlükte gör → yalnızca “bu satır çalıştı mı” sorusunu ölçer. Şu test %100 coverage üretir ve hiçbir şey doğrulamaz:

            Coverage %100, değeri sıfır
            @Test
            void fizzBuzzCalisir() {
            fizzBuzz(15); // assertion yok — satır çalıştı, coverage sayıldı
            }

            Mutasyon testi doğru soruyu sorar: kodu kasten bozarsam testler bunu yakalar mı? PIT gibi araçlar > işaretini >= yapar, bir return değerini değiştirir, bir if’i ters çevirir — ve hiçbir testin kırmızıya dönmediği her 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 →u rapor eder.

            PIT çalıştırma
            mvn org.pitest:pitest-maven:mutationCoverage
            # Çıktı: mutation score — coverage'dan çok daha dürüst bir sayı

            Simülatördeki suite güveni çubuğu kabaca bu fikri gösterir: kaç test gerçekten bir şeyi kanıtladı.

            Kafam karıştı, daha basit anlat

            Coverage yalnızca “bu satır çalıştı mı” diye sorar, “sonuç doğru mu” diye sormaz. Mutasyon testi kodu bilerek bozar ve testlerin bunu yakalayıp yakalamadığına bakar.

            Hızlı kontrolİleri

            TDD ile yazılmış bir kodu refactor ettin ve 20 test kırıldı. Bu ne anlama gelir?

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

            Tuzaklar: TDD her yerde gerekmez

            Sınırlarını bilmek, yöntemi savunmaktan daha değerli:

            • İyi uyduğu yerler: net girdi/çıktı olan iş kuralları, hata düzeltme (önce hatayı gösteren kırmızı test), algoritmalar, refactor güvenlik ağı.
            • Kötü uyduğu yerler: henüz nasıl görüneceğini bilmediğin keşifsel UI çalışması, atılacak prototipler, ağır dış bağımlılık içeren entegrasyon noktaları.

            Bir hata bildirimi geldiğinde ise TDD neredeyse her zaman doğru araçtır: önce hatayı yeniden üreten kırmızı testi yaz. Böylece hem düzelttiğini kanıtlamış hem de aynı hatanın geri gelmesini engellemiş olursun.

            Kendini sına

            Şimşek turu1/5

            İlk yazıldığı anda geçen bir test, kodun doğru olduğunu kanıtlar.

            Soru 1/3Orta

            TDD'de testi önce kırmızı görmenin asıl sebebi nedir?

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

            Aklında kalacak üç şey

            1. 1 Testi bir kez kırmızı görmek, onun gerçekten bir şeyi kontrol ettiğinin tek kanıtıdır. Tasarıma faydası ikincildir.
            2. 2 Refactor testler yeşilken yapılır: davranışın değişmediğini söyleyen tek şey geçen testlerdir.
            3. 3 Coverage bunu ölçmez: %100 coverage, hiç kırmızı görülmemiş testlerle de elde edilir. Farkı mutasyon testi (PIT) ölçer.
            Sonraki kapı Tek satır kod değiştirdin, Docker imajı yine de bütün bağımlılıkları baştan indiriyor. Neden? Docker Katman Cache ve İmaj Boyutu · 10 dk

            5 kart sonraki derste seni bekliyor

            0/5 kart bu dersten toplandı

            Bu dersin üstüne kurulanlar

            Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.