Test edilebilir kod daha iyi kod mu?


103

Düzenli olarak benim koduyla birim testleri yazma alışkanlığı olsun çalışılıyor ama okumuştum ilk yazmak için önemlidir test edilebilir kod . Bu soru test edilebilir kod yazmanın SOLID ilkelerine değinir, ancak bu tasarım prensiplerinin hiç test yazmayı planlamadan faydalı olup olmadığını (veya en azından zararlı olmadığını) bilmek istiyorum. Netleştirmek için - Yazma testlerinin önemini anlıyorum; bu onların yararlılığı ile ilgili bir soru değil.

Kafamdaki kargaşamı göstermek için, bu soruyu ilham eden parçada , yazar mevcut zamanı kontrol eden ve zamana bağlı olarak bir miktar değer veren bir fonksiyon örneği verir. Yazar bunu yanlış kod olarak işaret eder, çünkü dahili olarak kullandığı verileri (zaman) üretir ve böylece test etmeyi zorlaştırır. Yine de bana göre, zaman içinde bir argüman olarak geçen bir şey gibi görünüyor. Bir noktada değerin başlatılması gerekiyor ve neden tüketime en yakın değil? Ayrıca, aklımdaki yöntemin amacı, geçerli zamana bağlı olarak bir değer döndürmek , bu amacın değiştirilebileceğini / değiştirilmesi gerektiğini ima edeceğiniz bir parametre haline getirmektir. Bu ve diğer sorular, test edilebilir kodun "daha iyi" kodla eş anlamlı olup olmadığını merak etmeme yol açtı.

Test edilebilir kod yazmak , testlerin yokluğunda bile iyi bir uygulama mı?


Test edilebilir kod aslında daha kararlı mı? yinelenen olarak önerildi. Bununla birlikte, bu soru kodun "kararlılığı" hakkındadır, ancak kodun okunabilirlik, performans, birleştirme vb. Gibi diğer nedenlerden daha üstün olup olmadığını daha geniş bir şekilde soruyorum.


24
İşlevin idempotency denilen zamana geçmenizi gerektiren özel bir özelliği vardır . Bu tür bir işlev, belirli bir argüman değeri ile her çağrıldığında aynı sonucu üretecektir , bu da sadece daha test edilebilir, fakat daha iyi anlaşılabilir ve nedenini düşünmesi daha kolay hale getirir.
Robert Harvey,

4
"Daha iyi kod" u tanımlayabilir misiniz? "bakımı yapılabilir" mi, "kullanımı kolay IOC-Container-Magic" mi demek istiyorsun?
k3b

7
Sanırım hiçbir zaman bir test başarısızlığınız olmadı çünkü gerçek sistem saatini kullandı ve sonra saat dilimi kayması değişti.
Andy

5
Test edilemeyen koddan daha iyidir.
Tulains Córdova

14
O Idempotency demezdim @RobertHarvey, ben söyleyebilirim referans şeffaflık : eğer func(X)döner "Morning", daha sonra tüm tekrarlarını değiştirilmesi func(X)ile "Morning"programı değişmeyecek (yani arama. funcDeğerini döndürmek dışında başka bir şey yapmaz). Idempotency ya olduğunu gösterir func(func(X)) == X(tip-doğru değildir), ya da func(X); func(X);aynı yan etkiler yapar func(X)(ancak hiçbir yan etkisi burada vardır)
Warbo

Yanıtlar:


116

Birim testlerinin ortak tanımı ile ilgili olarak, hayır derim. Test çerçevesine uyması için bükülmesi gerekmesi nedeniyle basitleştirilmiş basit kodlar gördüm (örneğin, her yerde arayüzler ve IoC her yerde sihir tarafından açıkça görünmesi gereken arayüz çağrıları ve veri katmanlarının takip edilmesini zorlaştırıyor). Anlaşılması kolay kod veya ünite testi kolay olan kod arasındaki seçim göz önüne alındığında, her zaman bakım kodunu kullanıyorum.

Bu, test etmemek değil, araçları tam anlamıyla size uyacak şekilde sığdırmak anlamına gelmez. Test etmenin başka yolları da var (ama anlaşılması zor kod her zaman hatalı koddur). Örneğin, daha az tanecikli birim testleri oluşturabilirsiniz (örn. Martin Fowler'ın bir birimin genellikle bir yöntem değil bir sınıf olduğuna dair tutumu) veya programınıza otomatik entegrasyon testleri ile varabilirsiniz. Bunlar, test çerçevenizin yeşil keneler ile aydınlatılması kadar güzel olmayabilir, ancak testin kodundan sonra sürecin oyunlaştırılmasından sonra olduk, değil mi?

Aralarında iyi arayüzler tanımlayarak ve ardından bileşenin genel arayüzünü kullanan testler yazarak kodunuzu korumayı kolaylaştırabilir ve yine de birim testleri için iyi yapabilirsiniz; veya daha iyi bir test çerçevesi elde edebilirsiniz (kodun sahte olduğu için derlenmesini istemek yerine, çalışma zamanında işlevlerle alay eden işlevleri değiştiren). Daha iyi bir birim test çerçevesi, GetCurrentTime () işlevselliğini kendi çalışma zamanınızla kendi sisteminizle değiştirmenize izin verir, böylece yalnızca test aracına uyması için buna yapay sarmalayıcılar eklemenize gerek kalmaz.


3
Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
Dünya Mühendisi

2
Son paragrafınızın tarif ettiği şeyi yapmanıza izin veren en az bir dil bildiğimi fark etmeyi düşünüyorum: Python with Mock. Modülün içe aktarılması nedeniyle, anahtar kelimelerden başka bir şey sahte, hatta standart API yöntemleri / sınıfları / vb. Taklit edilebilir. Bu mümkün, ancak dilin bu tür bir esnekliği destekleyecek şekilde tasarlanmasını gerektirebilir.
jpmc26

5
Sanırım "test edilebilir kod" ile "test çerçevesine uygun kod [bükülmüş]" arasında bir fark olduğunu düşünüyorum. "Bükülmüş" kodun kötü olduğu ve iyi arayüzlü "test edilebilir" kodun iyi olduğu konusunda hemfikir olduğumu söylemek dışında, bu yorum ile nereye gittiğimden emin değilim.
Bryan Oakley

2
Düşüncelerimin bazılarını makalenin yorumlarında ifade ettim (burada uzun yorumlara izin verilmediğinden), bir göz atın! Açıkçası: Ben bahsedilen makalenin
yazarıyım

@ByanOakley ile aynı fikirdeyim. "Test edilebilir kod" endişelerinizin ayrıldığını gösterir: bir yönü (modül) diğer yönlerden etkilenmeden test etmek mümkündür. Bunun "projenizi destekleyecek özel test sözleşmelerini düzenlemekten" farklı olduğunu söyleyebilirim. Bu tasarım modellerine benzer: zorlanmamalıdırlar. Tasarım desenlerini doğru bir şekilde kullanan kod, güçlü kod olarak kabul edilir. Aynısı test prensipleri için de geçerlidir. Kodunuzu "test edilebilir" yapmak, projenizin kodunu aşırı şekilde döndürmekle sonuçlanırsa, yanlış bir şey yapıyorsunuz demektir.
Vince Emigh,

68

Test edilebilir kod yazmak, testlerin yokluğunda bile iyi bir uygulama mı?

Öncelikle, ilk olarak, testlerin yokluğu, kodunuzun test edilebilir olması veya test edilmemesinden çok daha büyük bir konudur. Ünite testlerinin yapılmaması, kodunuz / özelliğinizin bitmediği anlamına gelir.

Bunun dışında, test edilebilir kod yazmanın önemli olduğunu söyleyemem - esnek kod yazmak önemlidir . Esnek olmayan kodun test edilmesi zordur, bu nedenle yaklaşımda ve insanların buna ne söylediği konusunda çok fazla örtüşme var.

Bana göre kod yazarken her zaman bir öncelikler kümesi vardır:

  1. Çalışmasını sağlayın - kod yapması gerekeni yapmazsa, değersizdir.
  2. Bakım yapılabilir hale getirin - kodun bakımı mümkün değilse, hızlı bir şekilde çalışmayı durdurur.
  3. Esnek hale getirin - kod esnek değilse, iş kaçınılmaz olarak geldiğinde ve kodun XYZ yapıp yapamayacağını sorduğunda çalışmayı durdurur.
  4. Çabuk olun - kabul edilebilir bir seviyenin ötesinde, performans sadece sosdur.

Birim testleri, kod korunmasını sağlar, ancak yalnızca bir noktaya kadar. Üniteyi test etmek için kodu daha az okunabilir veya daha kırılgan hale getirirseniz, sayaç verimli olur. "Test edilebilir kod" genellikle esnek bir koddur, bu yüzden iyidir, ancak işlevsellik ya da bakım kolaylığı kadar önemli değildir. Şimdiki zaman gibi bir şey için, esnek yapmak iyidir ancak kodun doğru ve daha karmaşık şekilde kullanılmasını zorlaştırarak sürdürülebilirliği arttırır. Sürdürülebilirlik daha önemli olduğu için, daha az test edilebilir olsa bile, genellikle daha basit yaklaşıma doğru karar vereceğim.


4
Test edilebilir ve esnek arasında gösterdiğiniz ilişkiyi seviyorum - bu tüm sorunu benim için daha anlaşılır hale getiriyor. Esneklik, kodunuzun uyum sağlamasına olanak tanır, ancak zorunlu olarak anlamanız biraz daha soyut ve daha az sezgiseldir, ancak bu faydalar için değerli bir fedakarlıktır.
WannabeCoder

3
Bununla birlikte, birim test çerçevesinin bunlara doğrudan erişebilmesi için, özel olarak kamuya açık olması ya da paket düzeyinde olması gereken yöntemleri görüyorum. İdeal bir yaklaşımdan uzak.
jwenting

4
@WannabeCoder Elbette, sadece sonunda zaman kazandırırken esneklik katmaya değer. Bu yüzden her bir metodu bir arayüze karşı yazmıyoruz - çoğu zaman baştan beri çok fazla esneklik eklemek yerine kodu yeniden yazmak daha kolaydır. YAGNI hala son derece güçlü bir ilkedir - sadece “ihtiyaç duymayacağınız şeyin” ne olduğundan emin olun, geriye dönük olarak eklemeniz zamandan önce uygulamak yerine ortalama olarak daha fazla iş vermeyecektir . Tecrübelerime göre esnekliği en fazla olan YAGNI'yi takip etmeyen kod .
Luaan

3
"Ünite testlerine sahip olmamak, kodunuz / özelliğinizin bitmediği anlamına gelir" - Doğru. “Yapılan işin tanımı” takımın karar verdiği bir şeydir. Bir dereceye kadar test kapsamı içerebilir veya içermeyebilir. Ancak hiçbir yerde, herhangi bir test yapılmadığında bir özelliğin “yapılamayacağını” söyleyen katı bir gereklilik yoktur. Takım test gerektirebilir veya olmayabilir.
15’te

3
@Telastyn 10 yıldan uzun bir süredir gelişim içinde, hiçbir zaman bir birim test çerçevesini zorunlu kılan bir ekibim olmadı ve sadece ikisinde bile (ikisi de zayıf kapsamı vardı) olan bir ekibim olmadı. Bir yer, yazdığınız özelliğin nasıl test edileceğine dair bir kelime belgesi gerektiriyordu. Bu kadar. Belki de şanssızım? Birim karşıtı test değilim (Ciddiyim, SQA.SE sitesini değiştirdim, çok profesyonel bir birim sınavıyım!) Ancak açıklamalarınızın iddia ettiği kadar yaygın olduğunu bulamadım.
corsiKa,

50

Evet, bu iyi bir uygulamadır. Bunun nedeni, test edilebilirlik için test edilebilirlik değildir. Yanında getirdiği açıklık ve anlaşılabilirlik adına.

Kimse testleri kendisi umursamıyor. Büyük regresyon test süitlerine ihtiyacımız olması üzücü bir gerçektir, çünkü temellerimizi sürekli kontrol etmeden mükemmel kod yazabilecek kadar zeki değiliz. Yapabilirsek, test kavramı bilinmeyecekti ve bunların hepsi bir sorun olmayacaktı. Keşke yapabilseydim. Ancak deneyimler, neredeyse hepimizin yapamayacağını göstermiştir, bu nedenle kodumuzu kapsayan testler, işletme kodu yazmaktan zaman ayırsalar bile iyi bir şeydir.

Testlere sahip olmak iş kodumuzu testten bağımsız olarak nasıl geliştirir? Bizi işlevselliğimizi kolayca doğru olduğu kanıtlanan birimlere ayırmaya zorlayarak. Bu birimler, doğru yazmak, aksi takdirde yazmaktan çekinenlerden daha kolaydır.

Zaman örneğiniz iyi bir nokta. Sürece olarak sadece bunu programlanabilir sahip olmanın anlamı yok olduğunu düşünebilir geçerli zamanı dönen bir işlevi var. Bunu düzeltmek ne kadar zor olabilir? Ama kaçınılmaz programınız olacaktır kullanmak diğer kodunda bu işlevi, ve kesinlikle test etmek istediğiniz o farklı zamanlarda olmak üzere, farklı koşullar altında kod. Bu nedenle, işlevinizin geri dönme zamanını değiştirebilmek iyi bir fikirdir - tek hatlı currentMillis()aramaya güvenmemeniz nedeniyle değil , ancak o şarttaki aramayı kontrol eden koşullar altında doğrulamanız gerektiği için . Görüyorsunuz, kod test edilebilir olması, kendi başına bile olsa, bu kadar dikkati hak ediyor gibi görünmüyor.


Başka bir örnek, bir projenin kodunun bir bölümünü başka bir yere çıkarmak (nedense). İşlevselliğin farklı bölümleri ne kadar bağımsız olursa, tam olarak ihtiyaç duyduğunuz işlevselliği elde etmek o kadar kolay olur.
valenterry

10
Nobody cares about the tests themselves-- Yaparım. Testlerin, kodun yaptığı herhangi bir yorum veya benioku dosyasından daha iyi bir dokümantasyonu olduğunu tespit ediyorum.
jcollum

Test uygulamaları hakkında bir süredir yavaşça okudum (bir şekilde henüz hiç birim testi yapmamış olan) ve şunu söylemek zorundayım, kontrollü koşullar altında aramayı doğrulama hakkında son bölüm ve birlikte gelen daha esnek kod her türlü şeyi yerine oturttu. Teşekkür ederim.
plast1k

12

Bir noktada değerin başlatılması gerekiyor ve neden tüketime en yakın değil?

Çünkü bu kodu, dahili olarak oluşturulandan farklı bir değerle tekrar kullanmanız gerekebilir. Parametre olarak kullanacağınız değeri girebilme özelliği, bu değerleri yalnızca "şimdi" değil (kodu çağırdığınızda "şimdi" anlamına gelen) "istediğiniz zaman" temel alarak üretebilmenizi sağlar.

Kodun test edilebilir hale getirilmesi, iki farklı senaryoda (üretim ve test) kullanılabilen (başlangıçtan itibaren) kod yapmak anlamına gelir.

Temel olarak, testlerin yokluğunda kodun test edilebilir hale getirilmesi için bir teşvik olmadığını iddia ederken, yeniden kullanılabilir kod yazarken büyük avantajlar vardır ve ikisi eşanlamlıdır.

Ayrıca, aklımdaki yöntemin amacı, geçerli zamana bağlı olarak bir değer döndürmek, bu amacın değiştirilebileceğini / değiştirilmesi gerektiğini ima edeceğiniz bir parametre haline getirmektir.

Ayrıca, bu yöntemin amacının bir zaman değerine dayalı bir değer döndürmek olduğunu ve “şimdi” ye dayanarak bunu üretmeniz gerektiğini savunabilirsiniz. Bunlardan biri daha esnektir ve bu değişkeni seçmeye alışırsanız, zaman içinde kod yeniden kullanma oranınız artacaktır.


10

Bunu söylemek aptalca görünebilir, ancak kodunuzu test etmek istiyorsanız, o zaman evet, test edilebilir kod yazmak daha iyidir. Sen sor:

Bir noktada değerin başlatılması gerekiyor ve neden tüketime en yakın değil?

Kesin olarak, atıfta bulunduğunuz örnekte, bu kodu test edilemez kılar. Sadece günün farklı zamanlarında testlerinizin alt kümesini çalıştırmazsanız. Veya sistem saatini sıfırlarsınız. Veya başka bir geçici çözüm. Hepsi sadece kodunuzu esnek yapmaktan daha kötü.

Esnek olmamaya ek olarak, söz konusu küçük yöntemin iki sorumluluğu vardır: (1) sistem zamanını almak ve sonra (2) buna göre bir değer vermek.

public static string GetTimeOfDay()
{
    DateTime time = DateTime.Now;
    if (time.Hour >= 0 && time.Hour < 6)
    {
        return "Night";
    }
    if (time.Hour >= 6 && time.Hour < 12)
    {
        return "Morning";
    }
    if (time.Hour >= 12 && time.Hour < 18)
    {
        return "Afternoon";
    }
    return "Evening";
}

Sorumlulukları daha da parçalara ayırmak mantıklıdır, böylece kontrolünüzün ( DateTime.Now) dışında kalan kısmı kodunuzun geri kalanı üzerinde en az etkiye sahiptir. Bunu yapmak, sistematik olarak test edilebilir olmanın yan etkisi ile yukarıdaki kodu daha kolay hale getirecektir.


1
Bu yüzden, istediğiniz zaman "Gece" nin bir sonucu olup olmadığını kontrol etmek için sabahın erken saatlerinde test etmek zorunda kalacaksınız. Bu zor. 29 Şubat 2016'da tarih işlemenin doğru olup olmadığını kontrol etmek istediğinizi varsayalım ... Ve bazı iOS programcıları (ve muhtemelen diğerleri de) yılın başlangıcından önce veya kısa bir süre sonra işleri karıştıran bir acemi hatasıyla karşı karşıya kalırlar. bunun için test edin. Ve deneyimlerime göre, 2 Şubat 2020'de tarih işleme
konusunu

1
@ gnasher729 Kesinlikle benim açımdan. "Bu kodu test edilebilir hale getirmek" birçok (test) sorunu çözebilecek basit bir değişikliktir. Testi otomatikleştirmek istemiyorsanız, kodun olduğu gibi iletilebileceğini tahmin ediyorum. Ancak "test edilebilir" olduğunda daha iyi olurdu.
Eric King,

9

Kesinlikle bir bedeli var, ancak bazı geliştiriciler bedelinin orada olduğunu unuttukları için ödemeye çok alışıklar. Örneğin, artık bir yerine iki üniteniz var, ek bir bağımlılığı başlatmak ve yönetmek için çağrı koduna ihtiyacınız vardı ve GetTimeOfDaydaha fazla test edilebilir olsa da, yeni teknenizi tekrar test ederken aynı tekneye geri döndünüz IDateTimeProvider. Sadece iyi testler yaparsanız, faydalar genellikle maliyetlerden daha ağır basar.

Ayrıca, bir dereceye kadar, test edilebilir kod türlerini yazmak, kodunuzu daha sürdürülebilir bir şekilde oluşturmanızı teşvik eder. Yeni bağımlılık yönetimi kodu can sıkıcıdır, bu nedenle mümkünse tüm zamana bağlı işlevlerinizi birlikte gruplamak istersiniz. Bu, örneğin bir zaman sınırında bir sayfa yüklediğinizde, bazı öğelerin önceki zaman kullanılarak ve bazılarının sonraki zamanı kullanarak oluşturulmuş olması gibi hataları hafifletmeye ve gidermeye yardımcı olabilir. Geçerli saati almak için tekrarlanan sistem çağrılarını engelleyerek programınızı hızlandırabilir.

Tabii ki, bu mimari gelişmeler büyük oranda fırsatları farkeden ve bunları uygulayan birine bağlıdır. Birimlere çok yakından odaklanmanın en büyük tehlikelerinden biri daha büyük resmi görmemek.

Birçok birim test çerçevesi, çalışma zamanında sahte bir nesneyle maymun oluşturmanıza izin verir; bu, test edilebilirliğin avantajlarını tüm karmaşa olmadan elde etmenizi sağlar. C ++ 'da yapıldığını bile gördüm. Test edilebilirlik maliyetinin buna değmediği durumlarda bu yeteneğe dikkat edin.


+1 - yazı birimi testlerini kolaylaştırmak için tasarım ve mimariyi geliştirmeniz gerekir.
BЈовић

3
+ - önemli olan kodunuzun mimarisi. Daha kolay test sadece mutlu bir yan etkidir.
gbjbaanb

8

Test edilebilirliğe katkıda bulunan her bir özelliğin, test edilebilirlik bağlamı dışında istenmesi mümkün değildir - örneğin, alıntı yaptığınız zaman parametresi için teste bağlı olmayan bir gerekçeyle gelmekte zorlanıyorum - örneğin test edilebilirliğe katkıda bulunan özellikleri genel olarak söylerken; ayrıca, test edilebilirlikten bağımsız olarak iyi koda katkıda bulunur.

Genel olarak konuşursak, test edilebilir kod dövülebilir koddur. Küçük, kesikli, yapışkan, topak halindedir, bu yüzden bireysel bitler yeniden kullanım için çağrılabilir. İyi organize edilmiş ve iyi adlandırılmış (adlandırmaya daha fazla önem verdiğiniz bazı işlevleri test edebilmek için; testler yazmadıysanız, tek kullanımlık bir fonksiyonun adı daha az önemli olacaktır). Daha fazla parametrik olma eğilimindedir (zaman örneğiniz gibi), bu nedenle diğer amaçlardan amaçlanan amaçtan farklı olarak kullanılmaya açıktır. KURU, bu yüzden daha az karmaşık ve anlaşılması kolaydır.

Evet. Testten bağımsız olarak test edilebilir kod yazmak iyi bir uygulamadır.


DRY olduğu konusunda katılmıyorum - bir yöntemde GetCurrentTime'i sarma MyGetCurrentTime, test takımına yardımcı olmaktan başka bir faydası olmadan OS çağrısını çok fazla tekrarlıyor. Bu sadece en basit örneklerdir, gerçekte çok daha kötüye giderler.
gbjbaanb

1
"işletim sistemi çağrısının yararsız olarak yanıtlanması" - bir saatte bir sunucuda koşarak, farklı bir saat diliminde bir aws sunucusuyla konuşmaya başladığınızda ve bu durum kodunuzu kırdıktan sonra tüm kodunuzu girmeniz gerekir. UTC döndüren MyGetCurrentTime özelliğini kullanacak şekilde güncelleştirin. ; saat çarpıklığı, gün ışığından yararlanma ve işletim sistemi çağrısına körü körüne güvenmenin iyi bir fikir olmamasının başka nedenleri olabilir veya en azından başka bir yenisini bırakabileceğiniz tek bir nokta var.
Andrew Hill,

8

Kodunuzun gerçekten çalıştığını ispat edebilmek isterseniz, test edilebilir kod yazmak önemlidir .

Kodunuzu yalnızca belirli bir test çerçevesine uyacak şekilde çarpık çarpıtmalara çarpma konusundaki olumsuz düşüncelere katılıyorum.

Öte yandan, buradaki herkes, bir noktada ya da diğerinde, ele alması gereken, bir ya da daha fazla belirsiz olanı kesmeden, neredeyse dokunulmayacak olan 1.000 çizgi uzunluğunda sihirli işlevle uğraşmak zorunda kaldı. başka bir yerde (ya da kendi içinde bir yerde, bağımlılığın görselleştirilmesi neredeyse imkansız olan) belirgin bağımlılıklar ve neredeyse tanımlanamaz bir şekilde tanımlanabilir. Test çerçevelerinin devrilmiş hale geldiğine dair (liyakat olmayan bir fikir) düşüncesi düşük kaliteli, denenemez kod yazmak için ücretsiz bir lisans olarak alınmamalıdır.

Test odaklı geliştirme idealleri, örneğin sizi tek sorumluluk prosedürleri yazmaya itme eğilimindedir ve bu kesinlikle iyi bir şeydir. Şahsen, tek sorumluluk, satın alma tek gerçek kaynağı, kontrol kapsamı (acayip 'küresel değişkenler yok) satın almak ve kırılgan bağımlılıkları minimumda tutmak ve kodunuzun test edilebilir olacağını söylüyorum. Belirli bir test çerçevesi tarafından test edilebilir mi? Kim bilir. Ama sonra belki kendini iyi bir koda uyarlaması gereken test çerçevesi var, başka türlü değil.

Ancak sadece açık olmak gerekirse, başka bir insan tarafından kolayca anlaşılmadığı kadar zeki veya uzun ve / veya birbirine bağımlı kod iyi bir kod değildir. Ayrıca tesadüfen, kolayca test edilebilen bir kod değildir.

Özetime yaklaşırken, test edilebilir kod daha iyi kod mu?

Bilmiyorum, belki değil. Buradaki insanların bazı geçerli noktaları var.

Ancak daha iyi kodun test edilebilir kod olma eğiliminde olduğuna inanıyorum .

Ve ciddi çabalar kullanılmak üzere ciddi yazılım bahsediyoruz takdirde, nakliye denenmemiş kodunuz işverenin veya müşterilerinizin para ile yapıyor olabilir en sorumlu şey değildir.

Ayrıca bazı kodların diğer kodlardan daha zorlu testler gerektirdiği ve başka türlü davranması biraz saçma olduğu da doğrudur. Mekikteki hayati sistemlerle sizi araştıran menü sistemi denenmemişse, uzay mekiğinde astronot olmak isterdiniz? Veya reaktördeki sıcaklığı izleyen yazılım sistemlerinin test edilmediği nükleer bir tesiste çalışan mı? Öte yandan, basit bir salt okunur rapor üreten bir miktar kod, dokümantasyon dolu bir konteyner kamyonu ve bin birim testi gerektiriyor mu? Umarım değil. Sadece söylüyorum...


1
"daha iyi kod da test edilebilir kod olma eğilimindedir" Bu anahtardır. Test edilebilir yapmak daha iyi yapmaz. Onu daha iyi yapmak çoğu zaman bunu test edilebilir yapar ve testler genellikle size daha iyi hale getirmek için kullanabileceğiniz bilgiler verir ancak testlerin sadece varlığı kalitesini anlamına gelmez, ve (nadir) istisnaları vardır.
anaximander

1
Kesinlikle. Contrapositive düşünün. Test edilemeyen bir kod ise test edilmemiştir. Test edilmediyse, çalışıp çalışmadığını canlı bir durumdan başka nasıl biliyorsun?
pjc50

1
Tüm testler, kodun testleri geçtiğini kanıtlar. Aksi halde, birim test kodu hatasız olacaktır ve bunun böyle olmadığını biliyoruz.
wobbily_col

@anaximander Kesinlikle. En azından sadece testlerin varlığının onay kutularını kontrol etmekte olması durumunda, testlerin sadece varlığının düşük kalite kodu ile sonuçlanan bir kontrendikasyon olması ihtimali vardır. "Her fonksiyon için en az yedi ünite testi?" "Kontrol." Ancak kodun kalite kodu olması durumunda test etmenin daha kolay olacağına inanıyorum.
Craig,

1
... ancak bir bürokrasiyi testten çıkarmak, tam bir atık olabilir ve faydalı bilgiler veya güvenilir sonuçlar üretmez. Ne olursa olsun; Birisinin SSL Heartbleed hatalarını test etmesini diliyorum , tamam mı? ya da Apple gitme hatası böcek?
Craig,

5

Yine de bana göre, zaman içinde bir argüman olarak geçen bir şey gibi görünüyor.

Haklısın, ve alay ile kod test edilebilir yapabilir ve zaman (tanımsız cinas niyeti) geçen kaçının. Örnek kod:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

Şimdi, artık bir saniye boyunca olanları test etmek istediğinizi varsayalım. Söylediğin gibi, bunu aşırı derecede test etmek için (üretim) kodunu değiştirmek zorunda kalacaksın:

def time_of_day(now=None):
    now = now if now is not None else datetime.datetime.utcnow()
    return now.strftime('%H:%M:%S')

Python artık saniye destekliyse , test kodu şöyle görünür:

def test_handle_leap_second(self):
    actual = time_of_day(
        now=datetime.datetime(year=2015, month=6, day=30, hour=23, minute=59, second=60)
    expected = '23:59:60'
    self.assertEquals(actual, expected)

Bunu test edebilirsiniz, ancak kod gerektiğinden fazla karmaşıktır ve testler hala (bunun için bir değer geçmediğine olduğu güvenilir çoğu üretim kodu kullanıyor olacak kod dalı egzersiz olamaz now). Bir alay kullanarak bu sorunu çözmek . Orjinal üretim kodundan başlayarak:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

Test kodu:

@unittest.patch('datetime.datetime.utcnow')
def test_handle_leap_second(self, utcnow_mock):
    utcnow_mock.return_value = datetime.datetime(
        year=2015, month=6, day=30, hour=23, minute=59, second=60)
    actual = time_of_day()
    expected = '23:59:60'
    self.assertEquals(actual, expected)

Bu birkaç fayda sağlar:

  • Bağımlılıklarından time_of_day bağımsız olarak test ediyorsunuz .
  • Üretim koduyla aynı kod yolunu test ediyorsunuz .
  • Üretim kodu mümkün olduğunca basittir.

Bir yandan, gelecekteki alaycı çerçevelerin böyle şeyleri kolaylaştıracağı umulmaktadır. Örneğin, alaylı işleve bir dize olarak başvurmanız gerektiğinden, zaman time_of_dayiçin başka bir kaynak kullanmaya başladığında IDE'lerin otomatik olarak değiştirmesini sağlayamazsınız .


Bilginize: varsayılan argüman yanlış. Sadece bir kez tanımlanacaktır, böylece fonksiyonunuz her zaman ilk değerlendirildiği zamanı geri getirecektir.
ahruss

4

İyi yazılmış bir kodun kalitesi, değiştirilmesinin sağlam olmasıdır . Yani, bir gereksinim değişikliği ortaya çıktığında, koddaki değişiklik orantılı olmalıdır. Bu idealdir (her zaman elde edilemez), ancak test edilebilir kod yazmak bu hedefe yaklaşmamıza yardımcı olur.

Neden bize daha çok yaklaşmamıza yardımcı oluyor? Üretimde, kodumuz diğer tüm kodlarımızla bütünleşmek ve etkileşime girmek dahil olmak üzere üretim ortamında çalışır. Birim testlerinde bu ortamın çoğunu temizliyoruz. Kodumuz şimdi değişmekte güçlük çekiyor çünkü testler bir değişiklik . Üniteleri, üretimde kullanacağımızdan farklı girdiler (alaylar, hiç girmeyecek kötü girdiler vb.) İle farklı şekillerde kullanıyoruz.

Bu, sistemimizde değişimin olduğu gün için kodumuzu hazırlar. Diyelim ki zaman hesaplamamız bir zaman dilimine bağlı olarak farklı bir zaman almalı. Şimdi o zaman geçme yeteneğine sahibiz ve kodda herhangi bir değişiklik yapmak zorunda değiliz. Bir zaman geçirmek istemediğimiz ve o andaki zamanı kullanmak istemediğimizde, sadece varsayılan bir argüman kullanabiliriz. Bizim kodudur değiştirmek için sağlam test edilebilir çünkü.


4

Tecrübelerime göre, bir program oluştururken verdiğiniz en önemli ve en geniş kapsamlı kararlardan biri , kodu nasıl birimlere böldüğünüzdür (burada "birimler" en geniş anlamıyla kullanılır). Sınıf temelli bir OO dili kullanıyorsanız, programı uygulamak için kullanılan tüm iç mekanizmaları belirli bir sınıfa ayırmanız gerekir. Daha sonra her bir sınıfın kodunu bazı yöntemlere ayırmanız gerekir. Bazı dillerde, kodunuzu fonksiyonlara nasıl ayıracağınız seçimdir. Veya SOA işini yaparsanız, ne kadar hizmet inşa edeceğinize ve her bir servise nelerin gireceğine karar vermeniz gerekir.

Seçtiğiniz arıza tüm süreç üzerinde çok büyük bir etkiye sahiptir. İyi seçimler kodun yazılmasını kolaylaştırır ve daha az hataya neden olur (test etmeye ve hata ayıklamaya başlamadan önce bile). Değiştirmeyi ve bakımını kolaylaştırırlar. İlginçtir ki, iyi bir arıza bulduğunuzda, test etmenin genellikle fakir olandan daha kolay olduğu ortaya çıkar .

Bu neden böyle? Tüm nedenleri anlayabileceğimi ve açıklayabileceğimi sanmıyorum. Ancak bunun bir nedeni, iyi bir bozulmanın kaçınılmaz olarak, uygulama birimleri için ılımlı bir "tane büyüklüğü" seçmek anlamına gelmesidir. Tek bir sınıf / method / function / module / etc içerisinde çok fazla işlevsellik ve çok fazla mantık sıkıştırmak istemezsiniz. Bu, kodunuzu okumayı ve yazmayı daha kolay hale getirir, ancak aynı zamanda test etmeyi de kolaylaştırır.

Sadece bu değil. İyi bir iç tasarım, her bir uygulama biriminin beklenen davranışının (girdi / çıktı / vb.) Net ve kesin bir şekilde tanımlanabileceği anlamına gelir. Bu test için önemlidir. İyi bir tasarım genellikle her bir uygulama biriminin diğerlerine ılımlı sayıda bağımlılığı olacağı anlamına gelir. Bu, kodunuzu başkalarının okumasını ve anlamasını kolaylaştırır, ancak aynı zamanda test etmeyi de kolaylaştırır. Sebepler devam ediyor; belki başkaları yapamadığım nedenleri daha fazla ifade edebilirler.

Sorunuzdaki örneğe gelince, "iyi kod tasarımı" nın tüm dış bağımlılıkların (örneğin sistem saatine bağımlılık gibi) her zaman "enjekte edilmesi" gerektiğini söylemeye eşdeğer olduğunu sanmıyorum. Bu iyi bir fikir olabilir, ancak bu burada tarif ettiğimden ayrı bir konudur ve artılarını ve eksilerini değerlendirmeyeceğim.

Bu arada, geçerli saati döndüren sistem işlevlerine doğrudan çağrı yapsanız bile, dosya sistemine göre işlem yapsanız ve bu durumda, bu kodunuzu ayrı ayrı test edemeyeceğiniz anlamına gelmez. İşin püf noktası, sistem işlevlerinin dönüş değerlerini taklit etmenize izin veren standart kitaplıkların özel bir sürümünü kullanmaktır. Başkalarının bu teknikten bahsettiğini hiç görmedim, ancak birçok dilde ve geliştirme platformunda yapılması oldukça kolaydır. (Umarım dil çalışma zamanınız açık kaynak kodludur ve oluşturulması kolaydır. Kodunuzu çalıştırmak bir bağlantı adımı içeriyorsa, hangi kitaplıklara bağlı olduğunu kontrol etmek de kolaydır.)

Özet olarak, test edilebilir kod mutlaka "iyi" kod değildir, ancak "iyi" kod genellikle test edilebilirdir.


1

Birlikte yapacaksanız KATI ilkelere Eğer bu genişletmek, özellikle eğer iyi tarafta olacak ÖPÜCÜK , DRY ve YAGNI .

Benim için eksik olan bir nokta, bir yöntemin karmaşıklığının noktasıdır. Basit bir alıcı / ayarlayıcı yöntemi midir? O zaman sadece test çerçevenizi karşılamak için testler yazmak zaman kaybı olur.

Verileri manipüle ettiğiniz daha karmaşık bir yöntemse ve dahili mantığı değiştirmek zorunda olsanız bile çalışacağından emin olmak istiyorsanız, bir test yöntemi için iyi bir çağrı olacaktır. Çoğu zaman birkaç gün / hafta / ay sonra bir kod parçasını değiştirmek zorunda kaldım ve test olayına sahip olduğum için gerçekten çok mutlu oldum. Yöntemi ilk geliştirirken, test yöntemiyle test ettim ve çalışacağından emin oldum. Değişiklikten sonra birincil test kodum hala çalıştı. Bu yüzden yaptığım değişikliğin üretimde bazı eski kodları bozmadığından emindim.

Test yazmanın bir başka yönü de diğer geliştiricilere yönteminizi nasıl kullanacaklarını göstermektir. Bir geliştirici çoğu zaman bir yöntemin nasıl kullanılacağı ve dönüş değerinin ne olacağı ile ilgili bir örnek arayacaktır.

Sadece iki sentim .

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.