Uygulama yazdıktan sonra testte bir hata nasıl düzeltilir


21

Mantığı doğru uyguladıktan sonra test hala başarısız olursa TDD'deki en iyi eylem yolu nedir (testte bir hata olduğu için)?

Örneğin, aşağıdaki işlevi geliştirmek istediğinizi varsayalım:

int add(int a, int b) {
    return a + b;
}

Aşağıdaki adımlarda geliştirdiğimizi varsayalım:

  1. Test yaz (henüz işlev yok):

    // test1
    Assert.assertEquals(5, add(2, 3));
    

    Derleme hatasıyla sonuçlanır.

  2. Bir kukla işlev uygulaması yazın:

    int add(int a, int b) {
        return 5;
    }
    

    Sonuç: test1geçer.

  3. Başka bir test durumu ekle:

    // test2 -- notice the wrong expected value (should be 11)!
    Assert.assertEquals(12, add(5, 6));
    

    Sonuç: test2başarısız, test1hala geçiyor.

  4. Gerçek uygulamayı yaz:

    int add(int a, int b) {
        return a + b;
    }
    

    Sonuç: test1hala geçer, test2hala başarısız olur (o zamandan beri 11 != 12).

Bu özel durumda: daha iyi olurdu:

  1. düzeltin test2ve şimdi geçtiğini veya
  2. Uygulamanın yeni bölümünü silin (yani yukarıdaki 2. adıma geri dönün), düzeltin test2ve başarısız olmasına izin verin ve sonra doğru uygulamayı yeniden ekleyin (yukarıdaki 4. adım).

Yoksa daha zekice bir yolu var mı?

Örnek problemin önemsiz olduğunu anlasam da, iki durumun eklenmesinden daha karmaşık olabilecek jenerik davada ne yapılması gerektiği ile ilgileniyorum.

EDIT (@Thomas Junk'un cevabına cevaben):

Bu sorunun odağı, TDD'nin önerdiği şeydir, iyi kod veya testlerin (TDD yolundan farklı olabilir) başarılması için “en iyi evrensel uygulama” ne değildir.



5
Açıkça, TDD'nizde TDD yapıyor olmanız gerekir.
Blrfl,

17
Biri bana neden TDD’den şüphelendiğimi sorarsa, onları bu soruya yönelteceğim. Bu Kafkaesque.
Traubenfuchs

@Blrfl, Xibit’in bize söylediği şeyi »TDD’yi TDD’ye koydum, böylece TDD’yi yaparken TDD’yi yapabildim«: D
Thomas Junk

3
@Traubenfuchs Sorunun ilk bakışta aptalca göründüğünü kabul ediyorum ve "her zaman TDD yapmayı" savunucusu değilim, ancak testin başarısız olduğunu görmek için güçlü bir yarar olduğuna inanıyorum, sonra testin geçmesini sağlayan kod yazın. (sonuçta bu sorunun asıl amacı budur).
Vincent Savard

Yanıtlar:


31

Kesinlikle kritik olan şey, testi hem başarılı hem de başarısız olarak görmenizdir.

Testin başarısız olması için kodu silip sonra kodu yeniden yazmanız veya yalnızca daha sonra geri yapıştırmak için panoya gizlice sokmanız farketmez. TDD asla bir şeyi yeniden yazmak zorunda kalmayacağınızı söylemedi. Testin yalnızca başarılı olması durumunda geçtiğini ve sadece başarısız olması durumunda başarısız olduğunu bilmek istiyor.

Testi hem başarılı hem de başarısız olarak görmek, testi nasıl test ettiğinizdir. Asla görmediğin bir teste asla güvenme.


Red Bar'a Karşı Yeniden Yönlendirmek Red Bar bize bir çalışma testini yeniden düzenlemek için resmi adımlar atıyor:

  • Testi çalıştırın
    • Yeşil çubuğa dikkat edin
    • Test edilen kodu kır
  • Testi çalıştırın
    • Kırmızı çubuğa dikkat edin
    • Refactor testi
  • Testi çalıştırın
    • Kırmızı çubuğa dikkat edin
    • Test edilen kodu kır
  • Testi çalıştırın
    • Yeşil çubuğa dikkat edin

Ancak, çalışan bir testi tekrar gözden geçirmiyoruz. Bir araba testini dönüştürmeliyiz. Endişeli olan şey, yalnızca bu testin kapsadığı sırada girilen koddur. Bu kod geri alınmalı ve test düzeltildikten sonra tekrar kullanılmalıdır.

Durum böyle değilse ve kod kapsamı, kodu kapsayan diğer testlerden dolayı endişe duymuyorsa, testi dönüştürüp yeşil bir test olarak sunabilirsiniz.

Burada kod da geri döndürülüyor, ancak testin başarısız olmasına neden olacak kadar. Bu sadece buggy testi kapsamındayken girilen tüm kodları kapsayacak kadar değilse, daha büyük bir kod geri dönüşüne ve daha fazla teste ihtiyacımız var.

Yeşil bir test tanıtın

  • Testi çalıştırın
    • Yeşil çubuğa dikkat edin
    • Test edilen kodu kır
  • Testi çalıştırın
    • Kırmızı çubuğa dikkat edin
    • Test edilen kodu kır
  • Testi çalıştırın
    • Yeşil çubuğa dikkat edin

Kodu kırmak, kodu yorumlayabilir veya yalnızca daha sonra yapıştırmak için başka bir yere taşıyabilir. Bu bize testin kapsadığı kodun kapsamını gösterir.

Bu son iki koşuda normal kırmızı yeşil döngüye geri döndünüz. Kodu kesmek ve testi geçmek için sadece yazmak yerine yapıştırıyorsunuz. Bu yüzden testi geçmesi için yeterince yapıştırdığınızdan emin olun.

Buradaki genel örnek, testin renginin beklediğimiz şekilde değiştiğini görmektir. Bunun, güvenilmez bir yeşil testin kısaca gerçekleştiği bir durum yarattığını unutmayın. Rahatsız edilme ve bu adımda nerede olduğunuzu unutma konusunda dikkatli olun.

Benim teşekkür Rubberduck için kucaklayan Red Bar bağlantısını.


2
Bu cevabı en çok sevdim: Testin hatalı kodla başarısız olduğunu görmek önemlidir, bu yüzden kodu siler / yorumlar, testleri düzeltir ve başarısız görür, kodu geri koyarım (testleri sınamak için kasıtlı bir hata veririm) Test) ve çalışması için kodunu düzeltin. Tamamen silmek ve yeniden yazmak çok XP, ama bazen sadece pragmatik olmanız gerekir. ;)
GolezTrol

@GolezTrol Cevabımın da aynı şeyi söylediğini düşünüyorum, bu yüzden net olmadığı konusunda herhangi bir geri bildiriminizi takdir ediyorum.
jonrsharpe

@ jonrsharpe Cevabınız da iyidir ve bunu okumadan önce onu yendim. Ancak kodu geri alma konusunda çok katı olduğunuz yerde, CandiedOrange bana daha çekici gelen daha pragmatik bir yaklaşım önerir.
GolezTrol,

@GolezTrol Kodu nasıl geri döndüreceğimi söylemedim ; yorumla, kes ve yapıştır, sakla, IDE geçmişini kullan; gerçekten önemli değil. Önemli olan, neden yaptığınızdır: Böylece doğru başarısızlığı elde edip etmediğinizi kontrol edebilirsiniz . Umarım açıklığa kavuşturmak için düzenleme yaptım.
jonrsharpe

10

Ulaşmak istediğiniz genel amaç nedir ?

  • Güzel testler yapmak?

  • Doğru uygulamayı yapmak ?

  • TTD dini olarak doğru yapıyor mu?

  • Yukarıdakilerin hiçbiri?

Belki de testler ve testler ile olan ilişkinizi düşünüyorsunuz.

Testler , bir uygulamanın doğruluğu hakkında hiçbir garanti vermez . Tüm testlerin geçmesi, yazılımınızın yapması gerekeni yapıp yapmadığı hakkında hiçbir şey söylemez; Yazılımınızla ilgili hiçbir temel ifade vermez .

Örnek alarak:

Eklemenin "doğru" uygulaması, eşdeğer kod olacaktır a+b. Ve sürece kodunuzu olarak yapar göre, algoritma olduğunu söyleyebilirim doğru öyle ve ne de doğru uygulamıştır.

int add(int a, int b) {
    return a + b;
}

Açık ilk görüşte , ikimiz de bu olduğunu, kabul edeceğini olduğu bir ek uygulama.

Ama ne biz gerçekten yapıyoruz bu kodun bu, demiyorum olduğu uygulanması additionbunun belli bir dereceye kadar sadece davranacağını gibi: düşün tamsayı taşması .

Tamsayı taşması kodda gerçekleşir , ancak kavramında gerçekleşmez addition. Yani: Kodunuz kavramı gibi belli bir şekilde davranır addition, ancak değildir addition.

Bu oldukça felsefi bakış açısının birkaç sonucu vardır.

Ve birincisi, söyleyebileceğiniz gibi, testler, kodunuzun beklenen davranış varsayımlarından başka bir şey değildir . Kodunuzu test olarak, (belki) emin, sizin yapmak asla uygulama olan sağ , diyebilirsiniz en iyisi, bu senin kod vardı sunar veya karşılanmadığını sonuçları ne beklentileriniz; Kodunuzun yanlış olması, olması, testinizin yanlış olması veya olması, ikisinin de yanlış olması.

Faydalı testler , kodun ne yapması gerektiği konusundaki beklentilerinizi çözmenize yardımcı olur : beklentilerimi değiştirmediğim ve değiştirilen kod bana beklediğim sonucu verdiği sürece, tahmin ettiğim varsayımların yapıldığından emin olabilirim Sonuçlar işe yaramış gibi görünüyor.

Yanlış varsayımlarda bulunduğunuzda bu yardımcı olmaz; ama hey! en azından şizofreni önler: hiç olmaması gerektiğinde farklı sonuçlar beklemek.


tl; Dr.

Mantığı doğru uyguladıktan sonra test hala başarısız olursa TDD'deki en iyi eylem yolu nedir (testte bir hata olduğu için)?

Testleriniz, kodun davranışına ilişkin varsayımlardır. Uygulamanızın doğru olduğunu düşünmek için iyi bir nedeniniz varsa, testi düzeltin ve bu varsayımın geçerli olup olmadığını görün.


1
Bence genel hedeflerle ilgili soru oldukça önemli, bunu ortaya çıkardığınız için teşekkürler. Benim için en yüksek öncelik şu şekildedir: 1. doğru uygulama 2. "hoş" testler (ya da "yararlı" / "iyi tasarlanmış" testler demeyi tercih ederim). TDD'yi bu iki hedefe ulaşmak için olası bir araç olarak görüyorum. Dolayısıyla, TDD'yi mutlaka dinsel olarak takip etmek istemem ama, bu soru bağlamında çoğunlukla TDD perspektifiyle ilgileniyorum. Bunu açıklığa kavuşturmak için soruyu düzenleyeceğim.
Attilio

Öyleyse, taşma testlerini yapan ve gerçekleştiğinde geçen bir test yazar mıydınız, yoksa algoritma eklendiğinden ve taşma yanlış cevap verdiğinden dolayı başarısızlığa uğradı mı?
Jerry Jeremiah

1
@JerryJeremiah Amacım: Testlerinizin kapsaması gereken kullanım durumunuza bağlıdır. Bir sürü tek rakam eklediğiniz bir kullanım durumu için, algoritma yeterince iyidir . "Büyük sayılar" eklemenizin çok muhtemel olduğunu biliyorsanız datatype, kesinlikle yanlış seçimdir. Bir test şunu açıklayacaktır: Beklentiniz "büyük sayılar için işe yarar" olur ve bazı durumlarda karşılanamaz. O zaman soru, bu davalarla nasıl başa çıkılacağı olacaktır. Köşe davaları mı? Ne zaman evet, onlarla nasıl başa çıkılır? Belki bazı quard cümleleri daha büyük bir karmaşayı önlemeye yardımcı olur. Cevap bağlama bağlı.
Thomas Junk

7

Uygulama yanlışsa testin başarısız olacağını bilmelisiniz, bu uygulama doğru ise geçmeyle aynı değildir. Bu nedenle, kodu , testi düzeltmeden önce başarısız olmasını beklediğiniz bir duruma getirmeli ve beklediğiniz nedenlerden (yani 5 != 12) tahmin etmediğiniz bir şey yerine başarısız olduğundan emin olmalısınız .


Beklediğimiz nedenle testin başarısız olduğunu nasıl kontrol edebiliriz?
Basilevs

2
@Basilevs: 1. başarısızlığın nedeninin ne olması gerektiğine dair bir hipotez yapın; 2. testi çalıştırın; ve 3. ortaya çıkan başarısızlık mesajını okuyun ve karşılaştırın. Bazen bu, size daha anlamlı bir hata vermek için testi yeniden yazmanın yollarını da önerir (örneğin, her ikisi de aynı şeyi test etse bile , assertTrue(5 == add(2, 3))daha az kullanışlı bir çıktı verir assertEqual(5, add(2, 3))).
jonrsharpe

Bu ilkenin burada nasıl uygulanacağı henüz belli değil. Bir hipotezim var - test sabit bir değer döndürüyor, nasıl aynı testi tekrar denemek doğru olduğumu garanti eder? Açıkçası bunu test etmek için başka bir teste ihtiyacım var. Cevaplamak için açık bir örnek eklemenizi öneririm.
Basilevs

1
@Basilevs ne? Buradaki 3. adımdaki hipoteziniz "test başarısız olur çünkü 5 12'ye eşit değildir" olur . Testin yapılması, testin bu nedenle başarısız olup olmadığını, hangi durumda devam ettiğinizi veya başka bir nedenden dolayı, hangi durumda olduğunu tespit edersiniz. Belki de bu bir dil problemidir, fakat ne önerdiğiniz bana göre belirsiz.
jonrsharpe

5

Bu özel durumda, 12'yi 11'e değiştirirseniz ve test şimdi geçerse, testin yanı sıra uygulamanın test edilmesinde de iyi bir iş çıkardığınızı düşünüyorum, bu nedenle ek çemberlere geçmeye gerek kalmaz.

Ancak aynı sorun, kurulum kodunuzda bir hata yaptığınız gibi daha karmaşık durumlarda da ortaya çıkabilir. Bu durumda, testinizi düzelttikten sonra, muhtemelen testinizi başarısız kılacak şekilde uygulamanızı değiştirmeyi denemelisiniz ve ardından mutasyonu geri almalısınız. Uygulamayı geri almak, bunu yapmanın en kolay yoluysa, sorun değil. Senin örnekte, mutasyona olabilir a + biçin a + aya a * b.

Alternatif olarak, iddiayı hafifçe değiştirebilir ve testin başarısız olduğunu görebilirsiniz, bu testin test edilmesinde oldukça etkili olabilir.


0

Diyelim ki, bu en sevdiğiniz sürüm kontrol sistemi için bir durumdur:

  1. Kod değişikliklerinizi çalışma dizininizde tutarak testin düzeltilmesini uygulayın.
    İlgili mesajla tamamlayın Fixed test ... to expect correct output.

    Bununla birlikte git, git add -peğer test ve uygulama aynı dosyadaysa, bunun yerine iki dosyayı ayrı ayrı aşamalandırabilirsiniz.

  2. Uygulama kodunu kabul et.

  3. Testin gerçekten başarısız olduğundan emin olarak 1. adımda yapılan taahhüdü test etmek için zamanda geriye gidin .

Görüyorsunuz, bu şekilde başarısızlık testinizi test ederken uygulama kodunuzu kullanım dışı bırakmak için düzenleme becerinize güvenmezsiniz. İşinizi kurtarmak ve VCS'nin kayıtlı geçmişinin doğru bir şekilde hem başarısız hem de başarılı testi içermesini sağlamak için VCS'nizi kullanırsınız.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.