Testte mantıktan kaçınırken bir koleksiyon döndüren test yöntemi nasıl birimi


14

Veri nesneleri koleksiyonu oluşturmak için bir yöntem test sürüşü. Nesnelerin özelliklerinin doğru ayarlandığını doğrulamak istiyorum. Bazı özellikler aynı şekilde ayarlanacaktır; diğerleri ise koleksiyondaki konumlarına bağlı bir değere ayarlanacaktır. Bunu yapmanın doğal yolu bir döngü ile görünmektedir. Ancak Roy Osherove, birim testlerde mantığın kullanılmamasını şiddetle tavsiye eder ( Art of Unit Testing , 178). Diyor:

Mantık içeren bir test genellikle bir seferde birden fazla şeyi test eder, bu tavsiye edilmez, çünkü test daha az okunabilir ve daha kırılgandır. Ancak test mantığı, gizli bir hata içerebilecek karmaşıklık da ekler.

Testler, genel bir kural olarak, kontrol akışı olmayan, hatta değil try-catch, onay çağrılarıyla bir dizi yöntem çağrısı olmalıdır.

Bununla birlikte, tasarımımla ilgili yanlış bir şey göremiyorum (değerlerinin bazıları nerede oldukları sırayla bağlı olan veri nesnelerinin bir listesini nasıl oluşturuyorsunuz? - bunları tam olarak ayrı ayrı oluşturamaz ve test edemezsiniz). Tasarımımla test dostu olmayan bir şey var mı? Yoksa Osherove'nin öğretisine çok katı bir şekilde bağlı mıyım? Ya da bilmiyorum bazı gizli birim testi sihir bu sorunu aşar? (C # / VS2010 / NUnit dilinde yazıyorum, ancak mümkünse dile özgü olmayan yanıtlar arıyorum.)


4
Dönmemenizi tavsiye ederim. Testiniz üçüncü şeyin Çubuğu Frob olarak ayarlanmışsa, üçüncü şeyin Çubuğunun Frob olup olmadığını özel olarak kontrol etmek için bir test yazın. Bu tek başına bir test, doğrudan git, döngü yok. Testiniz 5 şeyden oluşan bir koleksiyon elde ediyorsanız, bu da bir testtir. Bu, asla bir döngünüzün olmadığı (açık veya başka türlü) anlamına gelmez, sadece sık sık ihtiyacınız yoktur. Ayrıca, Osherove'nin kitabını gerçek kurallardan daha fazla rehber olarak ele alın .
Anthony Pegram

1
@AnthonyPegram Kümeleri sıralanmamış - Frob bazen 3, bazen 2 olabilir. inTest "Frob mevcut bir koleksiyona başarıyla eklendi" ise, ona güvenemezsiniz, bir döngü (veya Python's gibi bir dil özelliği ) gerekir.
Izkata

1
@Izbata, sorusu özellikle sıralamanın önemli olduğunu belirtiyor. Onun sözleri: "diğerleri koleksiyondaki konumlarına bağlı bir değere ayarlanacak." Ekleme siparişi verilen C # 'da (başvurduğu dil) birçok koleksiyon türü vardır. Bu nedenle, bahsettiğiniz bir dil olan Python'daki listelerle siparişe de güvenebilirsiniz.
Anthony Pegram

Ayrıca, bir koleksiyonda Sıfırlama yöntemini test ettiğinizi varsayalım. Koleksiyonda dolaşmanız ve her bir öğeyi kontrol etmeniz gerekir. Koleksiyonun boyutuna bağlı olarak, bir döngüde test edilmemesi saçmadır. Veya diyelim ki bir koleksiyondaki her bir öğeyi artırması gereken bir şey test ediyorum. Tüm öğeleri aynı değere ayarlayabilir, artışınızı çağırabilir ve sonra kontrol edebilirsiniz. Bu test berbat. Bunlardan birkaçını farklı değerlere ayarlamanız, arama artışını ayarlamanız ve tüm farklı değerlerin doğru şekilde artırıldığını kontrol etmeniz gerekir. Koleksiyondaki tek bir rastgele öğeyi kontrol etmek çok şansa neden oluyor.
iheanyi

Bu şekilde cevap vermeyeceğim, çünkü bir gazilyon downvotes alacağım, ancak genellikle sadece toString()Koleksiyonu ve ne olması gerektiğini karşılaştırırım. Basit ve çalışır.
user949300

Yanıtlar:


16

TL; DR:

  • Testi yazın
  • Test çok fazla yaparsa, kod da çok fazla yapabilir.
  • Bu bir birim test olmayabilir (ama kötü bir test değildir).

Test için ilk şey dogmanın yararsız olmasıyla ilgilidir. Dogma ile ilgili bazı konulara ışık saçan bir şekilde dikkat çeken Testivus Yolu'nu okumaktan zevk alıyorum .

Yazılması gereken testi yazın.

Testin bir şekilde yazılması gerekiyorsa, bu şekilde yazın. Testi idealize edilmiş bir test düzenine zorlamaya çalışmak ya da hiç almamak iyi bir şey değildir. Bugün test eden bir teste sahip olmak, daha sonraki bir gün "mükemmel" bir testten daha iyidir.

Ayrıca çirkin test biraz işaret edecek:

Kod çirkin olduğunda, testler çirkin olabilir.

Çirkin testler yazmaktan hoşlanmıyorsunuz, ancak en çirkin kodun test edilmesi gerekiyor.

Çirkin kodun test yazmanızı engellemesine izin vermeyin, ancak çirkin kodun daha fazla yazmanızı engellemesine izin verin.

Bunlar uzun zamandır takip edenlere gerçekçilik olarak kabul edilebilir ... ve sadece düşünme ve test yazma yolunda kökleşmiş olurlar. Bu noktaya gelmemiş ve bu noktaya gelmeye çalışan insanlar için hatırlatıcılar yardımcı olabilir (hatta tekrar okuduğumda bazı dogmalara kilitlenmekten kaçınmamı sağlar).


Çirkin bir test yazarken, kodun çok fazla yapmaya çalıştığının bir göstergesi olabileceğini düşünün. Test ettiğiniz kod, basit bir test yazarak düzgün bir şekilde uygulanamayacak kadar karmaşıksa, kodu daha basit testlerle test edilebilecek daha küçük parçalara bölmeyi düşünebilirsiniz. Kişi her şeyi yapan bir birim testi yazmamalıdır (o zaman bir birim testi olmayabilir ). Tıpkı 'tanrı nesneleri' kötü olduğu gibi, 'tanrı birimi testleri' de kötüdür ve geri dönüp koda tekrar bakmanın bir göstergesi olmalıdır.

Sen gerektiğini Böyle basit testler aracılığıyla tüm makul kapsama kod yerine getirebilmelidir. Daha büyük sorularla ilgilenen testler uçtan uca yapılan testler ("Bu nesneyi xml'ye ekledim, web servisine, kurallar aracılığıyla, geri döndü ve paylaşılmadı") mükemmel bir testtir - ama kesinlikle değil bir birim sınaması (ve sınama yapmak için çağırdığı hizmetleri ve özel bellek veritabanlarında özelleştirilmiş olsa bile, tümleştirme sınama alanına girer). Yine de test için XUnit çerçevesini kullanabilir, ancak test çerçevesi onu birim test yapmaz.


7

Yeni bir cevap ekliyorum çünkü perspektifim orijinal soru ve cevabı yazdığımdan farklı; onları bir araya getirmek mantıklı değil.

Orijinal soruda dedim

Bununla birlikte, tasarımımla ilgili yanlış bir şey göremiyorum (değerlerinin bazıları nerede oldukları sırayla bağlı olan veri nesnelerinin bir listesini nasıl oluşturuyorsunuz? - bunları tam olarak ayrı ayrı oluşturamaz ve test edemezsiniz)

Burası yanlış yaptığım yer. Geçen yıl için fonksiyonel programlama yaptıktan sonra, şimdi bir akümülatörle bir toplama işlemine ihtiyacım olduğunu fark ettim. Daha sonra bir şey üzerinde çalışan saf bir işlev olarak işlevimi yazabilir ve koleksiyona uygulamak için bazı standart kütüphane işlevini kullanabilirim.

Benim yeni cevabım: fonksiyonel programlama tekniklerini kullanmak ve çoğu zaman bu problemden tamamen kaçınacaksınız. Tek bir şey üzerinde çalışmak için işlevlerinizi yazabilir ve bunları yalnızca son anda bir şeyler koleksiyonuna uygulayabilirsiniz. Ancak saflarsa, koleksiyonlara başvurmadan onları test edebilirsiniz.

Daha karmaşık mantık için, mülk tabanlı testlere yaslanın . Mantıkları olduğunda, test edilen kodun mantığından daha az ve ters olmalıdır ve her test küçük bir mantığın buna değdiğini vaka tabanlı bir birim testinden çok daha fazla doğrular.

Her şeyden önce her zaman türlerinize yaslanın . Mümkün olan en güçlü tipleri edinin ve bunları kendi yararınıza kullanın. Bu, ilk etapta yazmak zorunda olduğunuz test sayısını azaltacaktır.


4

Aynı anda çok fazla şeyi test etmeye çalışmayın. Koleksiyondaki her veri nesnesinin özelliklerinin her biri bir test için çok fazla. Bunun yerine şunu öneririm:

  1. Koleksiyon sabit uzunlukta ise, uzunluğu doğrulamak için bir birim testi yazın. Değişken uzunluk ise, davranışını karakterize edecek uzunluklar için birkaç test yazın (örn. 0, 1, 3, 10). Her iki durumda da, bu testlerdeki özellikleri doğrulamayın.
  2. Özelliklerin her birini doğrulamak için bir birim testi yazın. Koleksiyon sabit uzunlukta ve kısasa, her test için her bir öğenin bir özelliğine karşı iddia edin. Sabit uzunluklu ancak uzunsa, her biri bir özelliğe karşı savunmak için öğelerin temsili fakat küçük bir örneğini seçin. Değişken uzunlukta ise, nispeten kısa fakat temsili bir koleksiyon (yani belki üç eleman) oluşturun ve her birinin bir özelliğine karşı iddia edin.

Bu şekilde yapılması testleri devre dışı bırakmanın acı verici görünmeyecek kadar küçük olmasını sağlar. C # / Birim örneği, test edilen yöntem ICollection<Foo> generateFoos(uint numberOfFoos):

[Test]
void generate_zero_foos_returns_empty_list() { ... }
void generate_one_foo_returns_list_of_one_foo() { ... }
void generate_three_foos_returns_list_of_three_foos() { ... }
void generated_foos_have_sequential_ID()
{
    var foos = generateFoos(3).GetEnumerable();
    foos.MoveNext();
    Assert.AreEqual("ID1", foos.Current.id);
    foos.MoveNext();
    Assert.AreEqual("ID2", foos.Current.id);
    foos.MoveNext();
    Assert.AreEqual("ID3", foos.Current.id);
}
void generated_foos_have_bar()
{
    var foos = generateFoos(3).GetEnumerable();
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
    foos.MoveNext();
    Assert.AreEqual("baz", foos.Current.bar);
}

"Düz birim testi" paradigmasına alışkınsanız (iç içe yapılar / mantık yok), bu testler oldukça temiz görünür. Böylece, orijinal problemi döngülerden ziyade bir kerede çok fazla özelliği test etmeye çalışmak olarak tanımlayarak testlerde mantıktan kaçınılır.


1
Osherove, 3 ileriye sahip olmak için başınızı bir tepside tutar. ;) Başarısız olan ilk kişi, geri kalanını asla doğrulayamayacağınız anlamına gelir. Döngüyü gerçekten önlemediğinizi de unutmayın. Sadece açık bir şekilde yürütülen formuna genişlettiniz. Zor bir eleştiri değil, sadece test durumlarınızı mümkün olan en düşük seviyeye izole etmek için biraz daha fazla uygulama almak , bir şey başarısız olduğunda kendinize daha spesifik geri bildirim vermek , akla gelebilecek hala devam edebilen (veya başarısız olabilen) diğer davaları doğrulamaya devam etmek için bir öneri kendi özel geri bildirimleri).
Anthony Pegram

3
@AnthonyPegram Test başına bir iddia eden paradigmayı biliyorum. "Test one thing" mantrasını tercih ediyorum (Bob Martin tarafından test edilen tek teste karşı, Clean Code'da ). Yan not: "bekle" ile "iddia" arasındaki birim test çerçeveleri iyi (Google Testleri). Geri kalanlara gelince, neden önerilerinizi örneklerle tam bir cevaba bölmüyorsunuz? Bence yararlanabilirim.
Kazark
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.