TDD, savunma programlamasını gereksiz kılıyor mu?


104

Bugün bir meslektaşımla ilginç bir tartışma yaptım.

Ben bir savunma programcısıyım. " Bir sınıfın nesnelerinin sınıf dışından etkileşime girdiğinde geçerli bir duruma sahip olmasını sağlamalı " kuralının her zaman uyulması gerektiğine inanıyorum. Bu kuralın nedeni, sınıfın kullanıcılarının kim olduğunu bilmemesi ve yasadışı bir şekilde etkileşime girdiğinde tahminen başarısız olması gerektiğini bilmesidir. Bence bu kural tüm sınıflar için geçerli.

Bugün bir tartışmamın olduğu belirli bir durumda, kurucumun argümanlarının doğru olduğunu doğrulayan bir kod yazdım (örn. Bir tamsayı parametresi> 0 olmalıdır) ve ön koşul yerine getirilmezse bir istisna atılır. Öte yandan meslektaşım, böyle bir kontrolün gereksiz olduğuna inanıyor çünkü ünite testleri sınıfın herhangi bir yanlış kullanımını yakalayabiliyor. Ek olarak, savunma programlaması doğrulamalarının da ünite testine tabi tutulması gerektiğine inanıyor, bu nedenle savunma programlaması çok iş katıyor ve bu nedenle TDD için uygun değil.

TDD'nin savunma programlamasını değiştirebileceği doğru mu? Sonuç olarak parametre doğrulama (ve kullanıcı girişi demek istemiyorum) gereksiz mi? Yoksa iki teknik birbirini tamamlıyor mu?


120
Tamamen test edilmiş kütüphanenizi, kullanmak üzere bir müşteriye yapıcı kontrolü olmadan teslim edersiniz ve sınıf kontratını bozarlar. Şu an ne gibi testler yapıyorsun?
Robert Harvey

42
IMO bunun tersi yoldur. Defansif programlama, uygun ön ve pro koşullar ve zengin tip bir sistem testleri gereksiz kılar.
gardenhead

37
Sadece "İyi keder" diyen bir cevap gönderebilir miyim? Defansif programlama, çalışma zamanında sistemi korur. Sınamalar, yapıcılara ve diğer yöntemlere iletilen geçersiz argümanlar da dahil olmak üzere sınayıcının düşünebileceği tüm olası çalışma zamanı koşullarını kontrol eder. geçersiz argümanlar iletildiğinde gerçekleşen diğer kasıtlı davranış. Ancak testler, çalışma zamanında sistemi korumak için hiçbir şey yapmaz.
Craig

16
"ünite testleri sınıfın herhangi bir yanlış kullanımını yakalamalı" - dı, nasıl? Birim testleri size doğru argümanlar verilen davranışları ve yanlış argümanlar verildiğinde; Size verilecek bütün argümanları gösteremezler.
OJFord

34
Yazılım geliştirme konusundaki dogmatik düşüncenin zararlı sonuçlara yol açabileceğinin daha iyi bir örneğini gördüğümü sanmıyorum.
sdenham

Yanıtlar:


196

Saçma. TDD, kodları testleri geçmeye zorlar ve tüm kodu, çevresinde bazı testler yapmaya zorlar. Tüketicilerinizin yanlış kod çağırmasını engellemez, ayrıca programcıların eksik test durumlarını sihirli bir şekilde engellemez.

Hiçbir metodoloji kullanıcıları kodu doğru şekilde kullanmaya zorlayamaz.

Orada ise Çeki ekleyerek muhtemelen tarafından - Eğer mükemmel TDD Bilseydin önce bunu uygulamak için bir test ihtimaline karşı> 0 çek dinledim ve bu ele olurdu yapılacak hafif bir argüman. Ancak TDD yaptıysanız, gereksiniminiz (yapıcıdaki> 0) ilk önce başarısız olan bir test çantası olarak görünür. Böylece çekinizi ekledikten sonra size testi veriyoruz.

Bazı savunma koşullarının test edilmesi de mantıklıdır (mantık eklediniz, neden bu kadar kolay test edilebilir bir şeyi test etmek istemiyorsunuz?). Neden buna katılmıyor göründüğünden emin değilim.

Yoksa iki teknik birbirini tamamlıyor mu?

TDD testleri geliştirecek. Parametre validasyonu uygulamak onları pasif hale getirecektir.


7
Önkoşul doğrulamasının test edilmesi gerektiği inancına katılmıyorum, ancak meslektaşımın önkoşul onayını test etme ihtiyacı nedeniyle ortaya çıkan fazladan çalışmanın ilk önkoşul onayını oluşturmadığına dair bir argüman olduğu görüşüne katılmıyorum yer, yerleştirmek. Açıklamak için gönderimde değişiklik yaptım.
user2180613

20
@ user2180613 Önkoşulun arızasının uygun şekilde ele alındığını test eden bir test oluşturun: şimdi çek eklemek "fazla" iş değil, TDD tarafından testi yeşil yapmak için gerekli olan iş. Eğer meslektaşınızın görüşü, testi yapmanız, arızalandığınızı gözlemlemeniz ve ardından sadece önkoşul kontrolünü uygulamanız gerektiği takdirde, TDD-safari açısından bir noktaya sahip olabilir. Eğer sadece çeki tamamen görmezden gelmek için diyorsa, o aptal. TDD'de potansiyel arıza modları için test yazarken proaktif olamayacağınızı söyleyen hiçbir şey yok.
RM

4
@RM Önkoşul kontrolünü test etmek için bir test yazmıyorsunuz. Çağrılan kodun beklenen doğru davranışını test etmek için bir test yazıyorsunuz. Önkoşul kontroller testin bakış açısından doğru davranışı sağlayan opak bir uygulama detayıdır. Aranan kodda doğru durumu sağlamanın daha iyi bir yolunu düşünüyorsanız, geleneksel bir önkoşul kontrolü kullanmak yerine bunu yapın. Test Eğer başarılı olsun veya olmasın dışarı taşıyacak ve hala biliyor ya umursamayacak nasıl başardın.
Craig

@ user2180613 Bu harika bir gerekçe: D Eğer yazılım yazmadaki amacınız yazar ve çalıştırmanız gereken test sayısını azaltmak ise, herhangi bir yazılım yazmayın - sıfır testler!
Gusdor

3
Bu cevabın son cümlesi onu doğruluyor.
Robert Grant

32

Savunma programlaması ve birim testleri, hataları yakalamanın iki farklı yoludur ve her birinin farklı güçleri vardır. Yalnızca hataları tespit etmenin bir yolunu kullanmak, hata tespit mekanizmalarınızı kırılgan hale getirir. Her ikisini de kullanmak, herkesin karşı karşıya kaldığı bir API olmayan kodda bile, bir başkası tarafından kaçırılmış olabilecek hataları yakalayacaktır; örneğin, birileri genel API’ye geçirilen geçersiz veriler için bir birim testi eklemeyi unutmuş olabilir. Her şeyi uygun yerlerde kontrol etmek, hatayı yakalamak için daha fazla şans demektir.

Bilgi güvenliğinde buna Derinlikli Savunma denir. Birden fazla savunma katmanına sahip olmak, birinin başarısız olması durumunda, onu yakalayacak başkalarının da olmasını sağlar.

Sen: İş arkadaşınız bir konuda haklı gerektiğini sizin doğrulamaları test etmek, ancak bu "gereksiz iş" değildir. Diğer kodları test etmekle aynıdır, tüm kullanımların, hatta geçersiz olanların bile beklenen bir sonuç aldığından emin olmak istersiniz.


Parametre doğrulamasının bir önkoşul onaylama biçimi olduğunu ve birim testlerinin koşullu onaylama olduğunu söylemek doğru mudur, bu yüzden birbirlerini tamamlarlar mı?
user2180613

1
“Diğer kodları test etmekle aynı, tüm kullanımların, hatta geçersiz olanların bile beklenen bir sonuç aldığından emin olmak istiyorsun.” Bu. Hiçbir kod, yalnızca girişini işlemek için tasarlanmadığında iletilmemelidir. Bu "hızlı başarısız" ilkesini ihlal ediyor ve hata ayıklamayı kabus haline getiriyor.
jpmc26

@ user2180613 - gerçekten değil, fakat birim testlerden daha fazlası geliştiricinin beklediği arıza koşullarını kontrol ederken, savunma programlama teknikleri geliştiricinin beklemediği koşulları kontrol eder. Ünite testleri olabilir (ön koşulu kontrol arayana enjekte sahte bir nesne kullanılarak) ön koşullar doğrulamak için kullanılabilir.
Periata Breatta 24:16

1
@ jpmc26 Evet, başarısızlık ise test için “beklenen sonuç”. Bazı tanımsız (beklenmedik) davranışları sessizce sergilemek yerine, başarısız olduğunu göstermek için test yaparsınız.
KRyan

6
TDD kendi kodunuzda hata yakalar, savunma programlaması diğer kişilerin kodunda hata yakalar. Böylece TDD yeterince savunmanızı sağlamanıza yardımcı olabilir :)
00'da jwenting

30

TDD kesinlikle savunma programlamasının yerini almaz. Bunun yerine, tüm savunmanın yerinde olduğundan ve beklendiği gibi çalıştığından emin olmak için TDD'yi kullanabilirsiniz.

TDD'de, ilk önce bir test yazmadan kod yazmanız gerekmez - kırmızı-yeşil-refactor döngüsünü dini olarak izleyin. Bu, doğrulama eklemek istiyorsanız, önce bu doğrulamayı gerektiren bir test yazın. Söz konusu yöntemi negatif sayılarla ve sıfırla çağırın ve bir istisna atmasını bekleyin.

Ayrıca “refactor” basamağını da unutmayın. TDD test-iken tahrik , bu test-anlamına gelmez sadece . Yine de uygun tasarımı uygulamanız ve makul bir kod yazmanız gerekir. Savunma kodu yazmak, mantıklı bir koddur, çünkü beklentileri daha belirgin hale getirir ve kodunuz genel olarak daha sağlamdır - olası hataları erken tespit etmek onların hata ayıklamasını kolaylaştırır.

Fakat hataları bulmak için testler kullanmamız gerekmiyor mu? Beyanlar ve testler tamamlayıcıdır. İyi bir test stratejisi , yazılımın sağlam olduğundan emin olmak için çeşitli yaklaşımları bir araya getirir. Yalnızca ünite testi veya yalnızca entegrasyon testi veya yalnızca koddaki iddiaların tümü tatmin edici değil, yazılımınıza kabul edilebilir bir gayretle yeterli bir güven derecesine ulaşmak için iyi bir kombinasyona ihtiyacınız var.

Öyleyse, iş arkadaşınızla ilgili çok büyük bir kavramsal yanlış anlama vardır: Ünite testleri sınıfınızın kullanımlarını asla test edemez , yalnızca sınıfın kendisinin izolasyonda beklendiği gibi çalıştığını gösterir. Çeşitli bileşenlerin arasındaki etkileşimin işe yarayıp yaramadığını kontrol etmek için entegrasyon testlerini kullanırsınız, ancak olası test durumlarının birleşik patlaması her şeyi test etmeyi imkansız hale getirir. Entegrasyon testleri bu nedenle kendilerini birkaç önemli durumla sınırlandırmalıdır. Kenar vakalarını ve hata durumlarını da kapsayan daha ayrıntılı testler birim testleri için daha uygundur.


16

Savunma programlamasını desteklemek ve sağlamak için testler yapıldı

Defansif programlama, çalışma zamanında sistemin bütünlüğünü korur.

Testler (çoğunlukla statik) teşhis araçlarıdır. Çalışma zamanında, testleriniz görünürde hiçbir yerde değildir. Yüksek tuğladan bir duvar veya bir kaya kubbesi koymak için kullanılan iskele gibiler. Yapının dışında önemli parçalar bırakmazsınız, çünkü inşaat sırasında ayakta tutan bir iskeleye sahipsiniz. Tüm önemli parçaların yerleştirilmesini kolaylaştırmak için inşaat sırasında dayanan bir iskeleye sahipsiniz .

EDIT: Bir benzetme

Koddaki yorumlardaki analoji ne olacak?

Yorumların amacı vardır, ancak fazlalık olabilir, hatta zararlı olabilir. Örneğin, yorumlar hakkında kodla ilgili temel bilgileri içeri koyarsanız, kodu değiştirin, yorumlar en iyi ihtimalle alakasız ve en kötüsü zararlı olur.

Demek ki, kod tabanınızla ilgili birçok gerçek bilgiyi, TestA gibi testlerden geçirmeyin, ki bu yöntem boş bırakamaz ve MethodB'nin argümanı olmalıdır > 0. Sonra kod değişir. Şimdi A için null tamamdır ve B, -10 kadar küçük değerler alabilir. Mevcut testler artık işlevsel olarak yanlış, ancak geçmeye devam edecek.

Evet, kodu güncellerken testleri de güncellemelisiniz. Ayrıca kodu güncellerken yorumları da güncellemeniz (veya kaldırmanız) gerekir. Ama hepimiz biliyoruz ki bu şeyler her zaman olmaz ve bu hatalar yapılır.

Testler sistemin davranışını doğrular. Bu gerçek davranış, testlerin kendine özgü değil , sistemin kendisine özgüdür.

Ne yanlış gidebilir ki?

Testlerle ilgili amaç, yanlış olabilecek her şeyi düşünmek, doğru davranışı kontrol eden bir test yazmak ve ardından çalışma zamanı kodunu oluşturmak ve böylece tüm testleri geçmektir.

Bu, savunma programlamasının noktası olduğu anlamına gelir .

TDD sürücüleri testler kapsamlı olursa, savunma programlama.

Daha fazla test, daha savunucu programlama

Hatalar kaçınılmaz olarak bulunduğunda, hatayı gösteren koşulları modellemek için daha fazla test yazılır. Ardından kod, bu testleri geçmek için kodla sabitlenir ve yeni testler test odasında kalır.

İyi bir test seti, hem iyi hem de kötü argümanları bir işleve / yönteme aktaracak ve tutarlı sonuçlar bekleyecektir. Bu da, test edilen bileşenin kendisine iletilen argümanları onaylamak için önkoşul kontrolleri (savunma programlaması) kullanacağı anlamına gelir.

Genel olarak konuşursak ...

Örneğin, belirli bir prosedür için boş bir argüman geçersizse, en az bir test boş geçecek ve bir tür "geçersiz boş argüman" istisnası / hatası bekleyecek.

En az bir başka test elbette geçerli bir argüman geçirecek - ya da büyük bir diziden geçecek ve on beş geçerli argüman geçecek - ve ortaya çıkan durumun uygun olduğunu onaylayacaktır.

Bir sınama , bu boş argümanı geçmezse ve beklenen istisna ile tokatlanırsa (ve bu istisna, kodun kendisine iletilen durumu savunmacı bir şekilde kontrol ettiği için atıldı), o zaman boş bir sınıfın özelliğine atanmış veya gömülmüş olabilir Olmaması gereken bir tür koleksiyonda.

Bu , yazılım gönderildikten sonra, sınıfın geçtiği sistemin tamamen farklı bir bölgesinde, bazı uzak coğrafi yerel ayarlarda beklenmeyen davranışlara neden olabilir . Ve bu aslında kaçınmaya çalıştığımız bir şey, değil mi?

Daha da kötüsü olabilirdi. Geçersiz durumda olan sınıf örneği yalnızca daha sonra kullanılmak üzere sulandırıldığında bir arızaya neden olmak için serileştirilebilir ve saklanabilir. Tanrım, bilmiyorum, belki bir kapatma işleminden sonra yeniden başlatılamayan bir tür mekanik kontrol sistemidir, çünkü kendi kalıcı konfigürasyon durumunu seri hale getiremez. Veya sınıf örneği seri hale getirilebilir ve başka bir varlık tarafından oluşturulan tamamen farklı bir sisteme geçirilebilir ve bu sistem çökebilir.

Özellikle diğer sistemin programcıları savunmada kodlamadıysa.


2
Bu komik, aşağı oy çok hızlı geldi, aşağı indirmenin ilk paragrafın ötesinde okuyabileceği kesin bir yol var.
Craig

1
:-) Ben sadece ilk paragrafın ötesini okumadan oy kullandım, bu yüzden umarım bunu
telafi eder

1
Ben :-) yapabileceği en az görünüyordu (Aslında ben yaptım sadece emin olmak için kalanını okumak özensiz olmamalıdır -.! Özellikle böyle bir konuda)
SusanW

1
Muhtemelen olduğunu düşündüm. :)
Craig

Savunma kontrolü, derleme sırasında Kod Sözleşmeleri gibi araçlarla yapılabilir.
Matthew Whited

9

TDD'nin yerine genel olarak “yazılım testi” ve genel olarak “savunma programlaması” yerine, genel olarak “savunma programlaması” yerine, iddiaları kullanan savunma programlarının en sevdiğim yöntemlerinden bahsedelim.


Bu yüzden, yazılım testi yaptığımızdan, üretim beyannamesi üzerinde açıklayıcı ifadeler bırakmayı bırakmalıyız, değil mi? Bunun yanlış olduğu yolları saymama izin verin:

  1. İddialar isteğe bağlıdır, bu nedenle beğenmezseniz, sadece iddialarınızı devre dışı bırakarak sisteminizi çalıştırın.

  2. İddialar testlerin yapamayacağı şeyleri kontrol eder (ve etmemelidir.) Çünkü testler sisteminizin kara kutu görüntüsüne sahip olması gerekirken, iddialar beyaz kutu görünümündedir. (Elbette, içinde yaşadıkları için.)

  3. İddialar mükemmel bir dokümantasyon aracıdır. Hiç bir yorum, aynı şeyi iddia eden bir kod parçası kadar net değildi veya hiç olmayacaktı. Ayrıca, dokümantasyon kod geliştikçe modası geçmiş olma eğilimindedir ve hiçbir şekilde derleyici tarafından uygulanabilir değildir.

  4. Beyanlar test kodundaki hataları yakalayabilir. Hiç bir testin başarısız olduğu bir duruma rastladınız mı ve kimin yanlış olduğunu bilmiyorsunuz - üretim kodu veya test?

  5. İddialar testten daha uygun olabilir. Testler, işlevsel gereksinimler tarafından öngörülenleri kontrol eder, ancak kodun genellikle bundan daha teknik olan bazı varsayımlarda bulunması gerekir. İşlevsel gereksinim belgeleri yazan insanlar nadiren sıfıra bölünmeyi düşünürler.

  6. İddialar, sadece geniş çapta ipuçlarını test eden hataları belirtir. Bu nedenle, testiniz bazı önkoşulları belirler, uzun bir kod parçası çağırır, sonuçları toplar ve beklendiği gibi olmadığını bulur. Yeterli sorun giderme verildiğinde sonuçta olayların yanlış gittiği yeri tam olarak bulacaksınız, ancak iddialar genellikle önce onu bulacaktır.

  7. İddialar programın karmaşıklığını azaltır. Yazdığınız her kod satırı programın karmaşıklığını arttırır. İddialar ve final( readonly) anahtar sözcüğü, program karmaşıklığını azalttığını bildiğim tek iki yapıdır. Bu paha biçilmez.

  8. İddialar, derleyicinin kodunuzu daha iyi anlamasını sağlar. Lütfen bunu evde deneyin: void foo( Object x ) { assert x != null; if( x == null ) { } }derleyiciniz, durumun x == nulldaima yanlış olduğunu söyleyen bir uyarı vermelidir . Bu çok faydalı olabilir.

Yukarıdakiler, blogumdaki bir yazının özeti, 2014-09-21 "Beyanlar ve Testler"


Sanırım çoğunlukla bu cevaba katılmıyorum. (5) TDD'de test takımı şartnamedir. Testleri geçmek için en basit kodu yazman gerekiyor, başka bir şey yok. (4) Kırmızı-yeşil iş akışı, testin yapılması gerektiğinde başarısız olmasını ve amaçlanan işlevsellik mevcut olduğunda geçmesini sağlar. İddialar burada pek yardımcı olmuyor. (3,7) Dokümantasyon dokümantasyondur, iddialar değildir. Ancak varsayımları açık yaparak, kod daha fazla kendini belgeliyor hale gelir. Bunları yürütülebilir yorumlar olarak düşünürdüm. (2) Beyaz kutu testi geçerli bir test stratejisinin parçası olabilir.
amon

5
“TDD'de test paketi şartnamedir. Testlerin geçmesini sağlayan en basit kodu yazmanız gerekiyor, başka bir şey yok.”: Bunun her zaman iyi bir fikir olduğunu sanmıyorum: Cevapta belirtildiği gibi kodda doğrulamak isteyebileceğiniz ilave iç varsayım. Peki ya birbirlerini iptal eden iç hatalar? Testleriniz başarılı ancak kodunuzun içindeki birkaç varsayım yanlıştır ve bu daha sonra sinsi hatalara yol açabilir.
Giorgio

5

Yanıtların çoğunun kritik bir ayrım eksik olduğuna inanıyorum: Kodunuzun nasıl kullanılacağına bağlı.

Söz konusu modül, test ettiğiniz uygulamanın bağımsızlığından başka müşteriler tarafından kullanılacak mı? Üçüncü tarafların kullanması için bir kütüphane veya API sağlıyorsanız, kodunuzu yalnızca geçerli girdiyle çağırmalarını sağlayamazsınız. Tüm girişi doğrulamanız gerekir.

Ancak söz konusu modül yalnızca kontrol ettiğiniz kod tarafından kullanılıyorsa, arkadaşınızın bir noktası olabilir. Söz konusu modülün yalnızca geçerli girişle çağrıldığını doğrulamak için birim sınamalarını kullanabilirsiniz. Ön koşul kontrolleri hala iyi bir uygulama düşünülebilir, ama bir trade-off: Sana çöp Eğer durum için kontrol eden kod biliyorum ortaya asla, sadece kodların amaçlarını örtmektedir.

Önkoşul kontrollerinin daha fazla birim testi gerektirdiğini kabul etmiyorum. Bazı geçersiz giriş biçimlerini test etmeniz gerekmediğine karar verirseniz, işlevin önkoşul denetimleri içerip içermediği önemli değildir. Testlerin uygulama detaylarını değil davranışları doğrulaması gerektiğini unutmayın.


4
Eğer çağrılan prosedür girişlerin geçerliliğini doğrulamazsa (ki asıl tartışma olan) birim testleriniz, söz konusu modülün sadece geçerli girişle çağrıldığından emin olamaz . Özellikle, geçersiz girdiyle çağrılabilir, ancak test edilen durumlarda yine de doğru bir sonuç döndürmek için olabilir - test sonuçlarında devre dışı bırakılmış optimizasyonlarla beklenen sonucu geri getirebilecek tanımsız davranış, taşma vb. üretimde başarısız.
Peteris

@Peteris: C'deki gibi tanımsız davranışları mı düşünüyorsunuz? Farklı ortamlarda farklı sonuçlara sahip olan tanımlanmamış davranışların kullanılması açık bir şekilde bir hatadır, ancak önkoşul kontrolleriyle de önlenemez. Örneğin, bir işaretçi argüman noktalarını geçerli belleğe nasıl kontrol edersiniz?
JacquesB

3
Bu sadece en küçük dükkanlarda çalışacak. Ekibiniz altı kişiden öteye giderse, altı kişiden sonra, yine de doğrulama kontrollerine ihtiyacınız olacak.
Robert Harvey

1
@RobertHarvey: Bu durumda, sistem iyi tanımlanmış arayüzlere sahip alt sistemlere bölünmeli ve arayüzde giriş doğrulama gerçekleştirilmelidir.
JacquesB

bu. Koduna bağlı, bu kod takım tarafından kullanılacak mı? Takımın kaynak koduna erişimi var mı? Tamamen iç kod ise argümanları kontrol etmek sadece bir yük olabilir, örneğin, 0'ı kontrol edip sonra istisna atarsınız ve arayan daha sonra bu sınıfa istisna vb. Atabilir ve beklersiniz. Nesne asla 2 litreden önce filtrelendiklerinden 0 almazlar. Bu üçüncü şahıslar tarafından kullanılacak bir kütüphane kodu ise başka bir hikaye. Tüm kod tüm dünya tarafından kullanılacak şekilde yazılmaz.
Aleksander Fular

3

Bu argüman beni şaşırtıyor, çünkü TDD'yi uygulamaya başladığımda "nesne" şeklindeki birim testleri <geçersiz girdi> "2 veya 3 kat arttığında yanıt veriyor. Meslektaşınızın, bu tür birim testlerini başarılı bir şekilde, işlevleri doğrulanmadan başarılı bir şekilde geçmeyi nasıl başardığını merak ediyorum.

Bunun bir sonucu olarak, diğer testlerin argümanlarına aktarılacak kötü çıktılar üretmediğinizi gösteren sohbet durumu ispatlanması çok daha zor. İlk durumda olduğu gibi, bu durum büyük ölçüde kapsamlı vakaların kapsamına bağlıdır, ancak tüm işlev girişlerinizin, üniteyi çıktılarını test etmiş ve kullanıcı girişinden değil, test ettiği diğer işlevlerin çıktılarından gelmesi gerekliliğine ek gereksiniminiz vardır. üçüncü parti modülleri.

Başka bir deyişle, TDD'nin yaptığı şey, unutmaktan kaçınmanıza yardımcı olmak için doğrulama koduna ihtiyaç duymanızı engellemez .


2

Sanırım meslektaşınızın sözlerini cevapların çoğundan farklı olarak yorumluyorum.

Bana öyle geliyor ki tartışmanın:

  • Tüm kodlarımız birim test edildi.
  • Bileşeninizi kullanan tüm kod bizim kodumuzdur veya birim başkası tarafından test edilmemişse (açıkça belirtilmemiş, ancak şunu anlıyorum "birim testleri sınıfın yanlış kullanımını yakalamalıdır").
  • Bu nedenle, işlevinizin her arayanı için, bileşeninizi alay eden bir yerde bir birim testi vardır ve arayan, bu alay için geçersiz bir değer geçirirse test başarısız olur.
  • Bu nedenle, geçersiz bir değer iletildiğinde işlevinizin ne yaptığı önemli değildir, çünkü testlerimiz bunun gerçekleşmeyeceğini söylüyor.

Bana göre, bu argüman buna mantıklı geliyor, ancak olası her durumu kapsayacak şekilde birim testlerine çok fazla güveniyor. Basit gerçek şu ki,% 100 çizgi / dal / yol kapsama alanının , arayan tarafın geçebileceği her değeri zorunlu olarak uygulamaması , oysa arayanın olası tüm durumlarının% 100'ünü kapsaması (yani, girdilerinin tüm olası değerleri) ve değişkenler) hesaplamalı olarak mümkün değildir.

Bu nedenle, (testler devam ettiği sürece) hiçbir zaman hatalı değerler geçmemelerini ve ek olarak, bileşeninizin kötü bir değer iletildiğinde tanınabilir bir şekilde başarısız olmasını sağlamak için arayanları birim sınamasına tercih etme eğiliminde olurum . en azından tercih ettiğiniz dilde kötü değerleri tanımanız mümkün olduğu sürece). Bu, entegrasyon testlerinde problemler ortaya çıktığında hata ayıklamaya yardımcı olacak ve benzer şekilde sınıfınızın kod birimlerini bu bağımlılıktan izole etmede daha az titiz olan kullanıcılarına da yardımcı olacaktır.

Ancak, <= 0 değeri iletildiğinde işlevinizin davranışını belgeleyip test ederseniz, negatif değerlerin artık geçersiz olmadığına (en azından, hiçbir şekilde herhangi bir argümandan daha fazla geçersiz olmayacağına) throwdikkat edin. bir istisna atmak için de belgelenmiştir!). Arayanlar bu savunma davranışına güvenme hakkına sahiptir. Fonksiyonu - Dil bu her durumda en iyi senaryo olduğunu olabilir, olabilir izin vardır hayır "geçersiz girdileri", ama birim test yeterince olması gereken bir durum atma içine işlevini tahrik etmemeye bekliyoruz arayanlar onlar don sağlamak için' t Buna sebep olan değerleri geçmeyin.

Meslektaşınızın çoğu cevaptan biraz daha az yanlış olduğunu düşünmeme rağmen, aynı sonuca ulaşıyorum, ki bu iki teknik birbirini tamamlıyor. Savunma programlayın, savunma kontrollerinizi belgeleyin ve test edin. İş, yalnızca "gereksizdir", kodunuzun kullanıcıları hata yaptıklarında yararlı hata mesajlarından yararlanamazlarsa. Teoride, bütün kodları kendinizle bütünleştirmeden önce bütün kodlarını sınarlarsa ve testlerinde hiçbir zaman hata olmazsa, o zaman hata mesajlarını asla görmezler. Uygulamada TDD ve toplam bağımlılık enjeksiyonunu yapsalar bile, geliştirme sırasında hala araştırma yapabilirler veya testlerinde atlamalı olabilirler. Sonuç olarak, kodları mükemmel olmadan önce kodunuzu ararlar!


Arayanların kötü değerleri geçmediklerinden emin olmak için test etme vurgusu koymanın işi, çok sayıda bas bağımlılığı olan ve kaygıları net bir şekilde ayırmayan kırılgan koda kendisini ödünç veriyor gibi görünüyor. Bu yaklaşımın arkasındaki düşünce sonucu ortaya çıkacak kodu beğeneceğimi sanmıyorum.
Craig

@Craig: onun bağımlılıklarını alay yoluyla test için bir bileşeni izole varsa, o zaman neden olur, bu yola bakmak değil sadece bu bağımlılıkları için doğru değerleri geçirir olduğunu test? Ve eğer bileşeni izole edemiyorsanız, gerçekten endişelerinizi ayırdınız mı? Savunma kodlamasına katılmıyorum, ancak savunma kontrolleri arama kodunun doğruluğunu test ettiğiniz anlamına gelirse, bu bir karışıklıktır. Bu nedenle, ankete katılanın meslektaşının çeklerin fazla olduğu doğru olduğunu düşünüyorum, ancak bunu onları yazmamak için bir sebep olarak görmek yanlış :-)
Steve Jessop

Gördüğüm tek göz kamaştırıcı delik, hala yalnızca kendi bileşenlerimin, tamamen katılıyorum gereken bağımlılıklara geçersiz değerler alamayacağını test ettiğimi, ancak kaç tane işletme yöneticisinin özel bir karar vermesi gerektiğine karar verdiğini düşünüyorum. ortak bileşeni arayabilir mi? Bu aslında bana veritabanı tasarımını ve ORM'lerle olan tüm güncel aşk ilişkisini hatırlatıyor, bu da çoğu (daha genç) veritabanının veritabanlarının sadece aptal bir ağ deposu olduğunu ve kendilerini kısıtlamalar, yabancı anahtarlar ve saklı prosedürlerle korumaması gerektiğini ilan ediyor.
Craig

Gördüğüm diğer şey, bu senaryoda, tabii ki, gerçek bağımlılıklara değil, yalnızca sahte aramaları test ettiğinizdir. Nihayetinde, bağımlılıktaki kod, arayan numaradaki kodu değil, belirli bir geçirilen değerle uygun şekilde çalışabilen veya çalışamayan koddur. Bu yüzden bağımlılığın doğru olanı yapması gerekir ve bunu sağlamak için bağımlılığın yeterli bağımsız test kapsamı olması gerekir. Unutmayın, bahsettiğimiz bu testlere "birim" testleri denir. Her bağımlılık bir birimdir. :)
Craig

1

Genel arayüzler kötüye kullanılabilir ve kullanılabilir

İş arkadaşınızın "ünite testlerinin, sınıfın herhangi bir yanlış kullanımını yakalaması gerektiği" iddiası, özel olmayan herhangi bir arayüz için kesinlikle yanlıştır. Eğer bir public işlev tamsayı argümanlarıyla çağrılabilirse, o zaman herhangi bir tamsayı argümanıyla çağrılabilir ve çağrılabilir ve kod uygun şekilde davranmalıdır. Genel işlev imzası örneğin Java Double tipini kabul ederse, null, NaN, MAX_VALUE, -Inf hepsi olası değerlerdir. Senin birimin testler Bu testler bu sınıfı kullanır kodu test edemezsiniz çünkü bu kod henüz yazılmaz, çünkü sınıfın yanlış kullanımları yakalayamaz sizin tarafından yazılmış olabilir ve kesinlikle kapsamı dışında olacak senin birim testler .

Öte yandan, bu yaklaşım (umarım çok daha fazla sayıda) özel mülk için geçerli olabilir - eğer bir sınıf bazı gerçeklerin daima doğru olmasını sağlayabilirse (örneğin, özellik X hiç boş olamaz, tamsayı konumu maksimum uzunluğu aşmaz) A işlevi çağrıldığında, tüm önkoşul veri yapıları iyi oluşturulmuştur) o zaman bunu performans nedenleriyle tekrar tekrar doğrulamaktan kaçınmak ve bunun yerine birim testlerine güvenmek uygun olabilir.


Bunun başlığı ve birinci paragrafı doğrudur çünkü çalışma zamanında kodu uygulayacak birim testleri değildir. Bu neyse diğer çalışma zamanı kodu ve gerçek dünya koşulları ve kötü bir kullanıcı girişlerini değişen ve hack girişimleri kodu ile etkileşime girer.
Craig

1

Kötüye kullanıma karşı savunma , bir gereklilik nedeniyle geliştirilen bir özelliktir . (Tüm arayüzler kötüye kullanıma karşı sıkı kontroller gerektirmez; örneğin, çok dar kullanılan iç üniteler)

Bu özellik test gerektiriyor: kötüye kullanıma karşı savunma gerçekten işe yarıyor mu? Bu özelliği test etmenin amacı, bunu yapmadığını göstermeye çalışmaktır: çekleri tarafından yakalanmayan bir modülün yanlış kullanımına ulaşmak.

Özel kontroller gerekli bir özellik ise, bazı testlerin varlığının onları gereksiz kıldığını iddia etmek gerçekten de saçmadır. Parametre üç negatif olduğunda (diyelim) bir istisna atan bazı fonksiyonların bir özelliği ise, pazarlık yapılamaz; Bunu yapacak.

Bununla birlikte, meslektaşınızın, girdiler üzerinde belirli kontroller için bir zorunluluk olmadığı, kötü girdiler için belirli cevaplar veren bir durumdan dolayı gerçekten mantıklı geldiğinden şüpheleniyorum : bunun için yalnızca anlaşılmış bir genel şart olduğu sağlamlık.

Bazı üst düzey fonksiyonlara giriş kontrolleri, kısmen, zayıf veya kötü bir şekilde test edilmiş dahili kodları beklenmeyen parametreler kombinasyonlarından korumak için (kod iyi bir şekilde test edilirse kontroller gerekli olmaz: kod sadece " hava durumu "kötü parametreler".

İş arkadaşının fikrinde gerçekler var ve muhtemelen ne anlama geldiği şudur: Savunak kodlu ve tüm kötüye kullanımlara karşı ayrı ayrı test edilen çok sağlam alt seviye parçalardan bir işlev geliştirirsek, daha üst düzey işlevlerin olması mümkündür. kendi kapsamlı kontrolleri olmadan sağlam.

Eğer sözleşmesi ihlal edilirse, belki istisnalar ya da her neyse, üst düzey işlevlerin kötüye kullanımı ile sonuçlanır.

Bununla ilgili tek sorun, alt seviye istisnalarının, üst seviye arayüze özgü olmamasıdır. Bunun bir problem olup olmadığı gereksinimlerin ne olduğuna bağlıdır. Gereklilik basitçe "işlev yanlış kullanıma karşı sağlam olmalı ve çarpmak yerine bir istisna atsın ya da çöp verileriyle hesaplamaya devam etsin" ise, o zaman aslında üzerinde bulunduğu alt seviye parçaların tüm sağlamlığı ile karşılanabilir. inşa edilmiş.

İşlev, parametreleriyle ilgili çok spesifik, ayrıntılı hata bildirimi için bir gereksinime sahipse, daha düşük seviye kontrolleri bu gereklilikleri tam olarak karşılamaz. Yalnızca fonksiyonun bir şekilde patlamasını sağlarlar (kötü bir parametre kombinasyonu ile devam etmez, çöp sonucu verir). İstemci kodu, belirli hataları yakalamak ve bunları gidermek için özel olarak yazılmışsa, düzgün çalışmayabilir. İstemci kodunun kendisi, girdi olarak, parametrelerin dayandığı verileri elde ediyor olabilir ve bunları kontrol etme ve hatalı değerleri belgelendiği gibi belirli hatalara çevirme işlevini bekliyor olabilir (böylece bunları ele alabilir. Hatalar)) Başka bir hatadan ziyade işlenmemiş ve yazılım görüntüsünü durduramaz.

TP; DR: Meslektaşınız muhtemelen aptal değildir; aynı şey etrafında sadece farklı bakış açılarıyla birbirinizden geçmiş gibi konuşuyorsunuz, çünkü gereksinimler tamamen çivilenmemiş ve her birinizin "yazılı olmayan gereksinimlerin" ne olduğu konusunda farklı bir fikri var. Parametre kontrolünde belirli bir gereklilik olmadığı zaman, zaten detaylı kontrol kodlaması yapmanız gerektiğini düşünüyorsunuz; Meslektaşım, parametreler yanlış olduğunda sağlam düşük seviye kodunun patlamasına izin verin. Kod aracılığıyla yazılı olmayan gereksinimler hakkında tartışmak verimsizdir: kod yerine gereksinimler konusunda hemfikir olmadığınıza dikkat edin. Kodlama şekliniz, gereksinimlerin ne olduğunu düşündüğünüzü yansıtır; meslektaşının yolu, gereksinimler hakkındaki görüşünü temsil eder. Bu şekilde görürseniz, doğru ya da yanlış olanın olmadığı açıktır. t kodunun kendisinde; Kod sadece şartnamenin ne olması gerektiği konusunda fikriniz için bir vekildir.


Bu, gevşek gereksinimlerin neler olabileceğine dair genel bir felsefi zorlukla bağdaştırılır. Bir işleve önemli izin verilirse ancak tamamen serbest olmayan saltanatın hatalı biçimlendirilmiş girdiler verildiğinde keyfi davranması gerekiyorsa (örneğin, bir görüntü kod çözücünün boş zamanlarında - isteğe bağlı olarak piksellerin bazı kombinasyonlarını üretmesi veya anormal bir şekilde sonlandırılması garanti edilirse, gereksinimleri karşılarsa) Ancak, kötü amaçlı hazırlanmış girişlerin rasgele kod yürütmesine izin verebilirse), hiçbir girişin kabul edilemez davranışlar üretmemesini sağlamak için hangi test durumlarının uygun olacağı belirsiz olabilir.
supercat,

1

Testler, sınıf sözleşmenizi tanımlar.

Sonuç olarak , bir testin bulunmaması tanımsız davranış içeren bir sözleşmeyi tanımlar . Yani geçerken içinnullFoo::Frobnicate(Widget widget) ve anlatılmamış çalışma zamanı tahribat gelişir, kendi sınıfının sözleşme dahilinde devam etmektedir.

Daha sonra, mantıklı bir seçim olan “tanımsız davranış olasılığını istemiyoruz” kararını veriyorsunuz. Bu, geçebilmek nulliçin beklenen bir davranışın olması gerektiği anlamına gelirFoo::Frobnicate(Widget widget) .

Ve siz de bu kararı bir

[Test]
void Foo_FrobnicatesANullWidget_ThrowsInvalidArgument() 
{
    Given(Foo foo);
    When(foo.Frobnicate(null));
    Then(Expect_Exception(InvalidArgument));
}

1

İyi bir dizi test dıştan uygulayacaktır sınıfınızın arayüzünü kullanır ve bu tür yanlış kullanımların doğru tepki vermesini sağlar (bir istisna veya "doğru" olarak tanımladığınız şey). Aslında, bir sınıf için yazdığım ilk sınama durumu, yapıcısını aralık dışı argümanlarla çağırmaktır.

Tam olarak test edilmiş bir yaklaşımla ortadan kaldırılma eğiliminde olan savunma programlarının türü, harici kod tarafından ihlal edilemeyen dahili değişmezlerin gereksiz şekilde doğrulanmasıdır .

Bazen kullandığım faydalı bir fikir, nesnenin değişmezlerini test eden bir yöntem sağlamak; Yırtma yönteminiz, nesne üzerindeki dış eylemlerinizin değişmezleri asla kırmadığını doğrulamak için çağırabilir.


0

TDD'nin testleri kodun geliştirilmesi sırasında hatalar yakalayacaktır .

Defansif programlamanın bir parçası olarak tanımladığınız sınır kontrolü , kod kullanımı sırasında hatalar yakalayacaktır .

İki alan aynıysa, yazdığınız kod yalnızca bu özel proje tarafından yalnızca dahili olarak kullanılırsa, TDD'nin tanımladığınız savunma programlama sınırlarının gerekliliğini ortadan kaldıracağı, ancak yalnızca bu türleri Sınırların kontrolü TDD testlerinde özel olarak yapılır .


Spesifik bir örnek olarak, TDD kullanılarak bir finansal kod kütüphanesinin geliştirildiğini varsayalım. Testlerden biri, belirli bir değerin asla negatif olamayacağını iddia edebilir. Bu, kütüphanenin geliştiricilerinin, özellikleri uygularken sınıfları yanlışlıkla yanlış kullanmamalarını sağlar.

Ancak, kitaplık yayınlandıktan ve onu kendi programımda kullandıktan sonra, bu TDD testleri benden olumsuz bir değer atamamı engellemiyor (maruz kaldığını varsayarak). Sınır kontrolü yapar.

Benim demek olduğunu kod sadece hiç (TDD altında) daha büyük bir başvurunun gelişiminin bir parçası olarak dahili olarak kullanıldığı takdirde, diğer programcılar tarafından kullanılan bir kütüphane olacak eğer bir TDD assert, negatif değer sorunu çözmek olabilir iken TDD olmadan çerçeve ve testler , sınır kontrol konuları.


1
Aşağı oylamadım, ama bu tür bir argümana ince ayrımlar eklemenin suyu kirlettiği fikrine katılıyorum.
Craig

@Craig Eklemiş olduğum belirli örnekle ilgili görüşlerinizi merak ediyorum.
Blackhawk

Örneğin özgüllüğünü seviyorum. Hala sahip olduğum tek endişe tüm tartışmaya genel. Örneğin; ekip boyunca yeni bir geliştirici geliyor ve bu finansal modülü kullanan yeni bir bileşen yazıyor. Yeni adam sistemin bütün karmaşıklıklarının farkında değil; sistemin nasıl çalışması gerektiği konusunda her türlü uzmanlık bilgisinin test edilmekte olan kod yerine testlere gömülü olduğu gerçeğini bir yana.
Craig

Böylece yeni adam / gal bazı hayati testler oluşturmayı özlüyor ve siz de testlerinizde artıklık var - sistemin farklı bölümlerindeki testler aynı koşulları kontrol ediyor ve zaman zaman uygun sonuçlar ortaya koymak yerine zaman geçtikçe tutarsızlaşıyor. önkoşul, eylemin bulunduğu kodda denetler.
Craig

1
Bunun gibi bir şey. Bunun dışındaki birçok argüman, çağıran kod için yapılan testlerin tüm kontrolleri yapmasıyla ilgili. Ancak, eğer herhangi bir dereceye kadar katılımınız varsa, aynı kontrolleri bir çok farklı yerden yaptınız, ve bu başlı başına bir bakım meselesi. Bir prosedür için geçerli girdilerin aralığı değişirse, ancak farklı bileşenler kullanan testler için o alan için alan bilgisine sahipseniz? Hala tamamen savunma programlarından yanayım ve çözülmesi gereken performans sorunlarının olup olmadığını ve ne zaman çözüleceğini belirlemek için profil kullanıyorum.
Craig

0

TDD ve savunma programlaması el ele gidiyor. Her ikisini de kullanmak gereksiz değil, aslında tamamlayıcıdır. Bir işleve sahip olduğunuzda işlevin açıklandığı gibi çalıştığından ve bunun için testler yazdığınızdan emin olmak istersiniz; Kötü bir giriş, kötü geri dönüş, kötü durum, vb. durumlarında ne olduğunu kapsamazsanız, testlerinizi yeterince sağlam bir şekilde yazmazsınız ve tüm testleriniz başarılı olsa bile kodunuz kırılgan olacaktır.

Gömülü bir mühendis olarak, birlikte iki byte eklemek ve sonucu şöyle döndürmek için bir işlev yazma örneğini kullanmaktan hoşlanıyorum:

uint8_t AddTwoBytes(uint8_t a, uint8_t b, uint8_t *sum); 

Şimdi sadece basitçe *(sum) = a + byapsaydınız işe yarar , ama sadece bazı girdilerle. a = 1ve b = 2yapacaktı sum = 3; Ancak, toplamın büyüklüğü bir bayt olduğundan a = 100ve taşma nedeniyle b = 200yapacağı için sum = 44. C'de, bu fonksiyonun başarısız olduğunu belirtmek için bu durumda hata döndürürsünüz; bir istisna atmak kodunuzda aynı şeydir. Başarısızlıkları göz önünde bulundurmamak veya bunları nasıl ele alacağınızı test etmek uzun süren işe yaramayacaktır, çünkü bu koşullar meydana gelirse, ele alınmayacak ve herhangi bir sayıda soruna neden olabilir.


Bu iyi bir görüşme sorusu örneğine benziyor (neden bir dönüş değeri ve bir "çıkış" parametresi var - ve sumboş bir işaretçi olduğunda ne olur ?).
Toby Speight
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.