Kırılgan Ünite Testlerinden Kaçının?


24

3.000'e yakın teste yazdık - veriler kodlanmış, kodların yeniden kullanımı çok zor. Bu metodoloji bizi kıçından ısırmaya başladı. Sistem değiştikçe, kırılan testleri düzeltmek için daha fazla zaman harcıyoruz. Birim, entegrasyon ve fonksiyonel testlerimiz var.

Aradığım şey yönetilebilir ve sürdürülebilir testler yazmak için kesin bir yol.

çerçeveler


Programmers.StackExchange, IMO ...
IAbstract 20:01

Yanıtlar:


21

Onları "kırık birim testleri" olarak düşünmeyin, çünkü değiller.

Bunlar, programınızın artık desteklemediği özelliklerdir.

"Testleri düzeltmek" olarak değil, "yeni gereksinimleri tanımlamak" olarak düşünmeyin.

Testler önce uygulamanızı belirtmelidir, aksi durumda değil.

İşe yaradığını bilene kadar çalışan bir uygulamanız olduğunu söyleyemezsiniz. Test edene kadar işe yaradığını söyleyemezsin.

Size rehberlik edebilecek birkaç not:

  1. Testler ve test edilen sınıflar kısa ve basit olmalıdır . Her test sadece yapışkan bir fonksiyonellik parçasını kontrol etmelidir. Yani, zaten diğer testlerin kontrol ettiği şeyleri önemsemiyor.
  2. Testler ve nesneleriniz, bir nesneyi değiştirirseniz, yalnızca bağımlılık grafiğini aşağıya doğru değiştireceğiniz ve bu nesneyi kullanan diğer nesneler etkilenmeyecek şekilde gevşek bir şekilde birleştirilmelidir.
  3. Yanlış şeyler oluşturup test ediyor olabilirsiniz . Nesneleriniz kolay arayüz oluşturma veya kolay uygulama için üretildi mi? İkincisi ise, eski uygulamanın arayüzünü kullanan bir çok kodu değiştirirken kendinizi bulacaksınız.
  4. En iyi durumda, kesinlikle Tek Sorumluluk ilkesine bağlı kalın. Daha kötü durumda, Arayüz Ayrıştırma ilkesine bağlı kalın. Bkz. KATI İlkeleri .

5
+1 içinDon't think of it as "fixing the tests", but as "defining new requirements".
StuperUser

2
+1 Testler uygulamanızı önce belirtmelidir, aksi şekilde
yapılmamalıdır

11

Tarif ettiğiniz şey aslında bu kadar kötü bir şey olmayabilir, ancak testlerinizin keşfettiği sorunları daha derinleştiren bir işaretçi olabilir.

Sistem değiştikçe, kırılan testleri düzeltmek için daha fazla zaman harcıyoruz. Birim, entegrasyon ve fonksiyonel testlerimiz var.

Kodunuzu değiştirebilir ve Testlerinizi istiyorsanız değil kırmak, bu benim için şüpheli olurdu. Meşru bir değişim ile bir hata arasındaki fark, sadece talep edilmesi, talep edilmesi (TDD'nin varsayılması) testleriniz tarafından tanımlanmış olmasıdır.

veri kodlanmış.

Testlerde sabit kodlanmış veriler iyi bir şeydir. Testler ispat olarak değil, sahte olarak çalışır. Çok fazla hesaplama varsa, testleriniz totolojiler olabilir. Örneğin:

assert sum([1,2,3]) == 6
assert sum([1,2,3]) == 1 + 2 + 3
assert sum([1,2,3]) == reduce(operator.add, [1,2,3])

Soyutlama ne kadar yüksek olursa, algoritmaya o kadar yaklaşırsınız ve bu sayede akutal uygulamayı kendisiyle karşılaştırmaya daha yakın olursunuz.

kodun çok az yeniden kullanılması

Testlerde kodun en iyi şekilde yeniden kullanımı assertThat, testler basit olduğu için jUnits'te olduğu gibi 'Çekler' şeklindedir . Bunun yanı sıra, eğer kodları paylaşmak için testler yeniden düzenlenebilirse, test edilen muhtemel gerçek kod da olabilir , böylece testler yeniden düzenlenmiş tabanı test edenlere indirgenir.


Vekilin aynı fikirde olmadığını bilmek istiyorum.
keppla

keppla - Ben düşürücü değilim, ancak genellikle modelde nerede olduğuma bağlı olarak, birim seviyesinde verileri test etmek için nesne etkileşimi test etmeyi tercih ederim. Verileri test etmek entegrasyon düzeyinde daha iyi çalışır.
Ritch Melton

@keppla Toplam öğeleri belirli kısıtlanmış öğeler içeriyorsa, siparişi farklı bir kanala yönlendiren bir sınıfa sahibim. Sahte bir emir yarattım, ikisini kısıtlı olan 4 maddeyle dolduruyorum. Kısıtlanan maddelere eklenince, bu test benzersizdir. Ancak, sahte bir emir oluşturma ve iki normal öğe ekleme adımları, başka bir testin sınırlandırılmamış ürün iş akışını test eden aynı kurulumdur. Bu durumda, siparişin müşteri verileri kurulumuna ihtiyaç duyması ve kurulum vb. Adresleri olması gerekiyorsa, öğelerle birlikte kurulum yardımcılarının yeniden kullanılması için bu iyi bir durum söz konusu değildir. Neden sadece yeniden kullanım iddia ediyor?
Asif Şiraz

6

Ben de bu sorunu yaşadım. Benim geliştirilmiş aşağıdaki gibi bir yaklaşım olmuştur:

  1. Bir şeyi test etmenin tek iyi yolu olmadıkça ünite testi yazmayın .

    Ünite testlerinin en düşük tanı ve tamir süresi maliyeti olduğunu kabul etmeye hazırım. Bu onları değerli bir araç yapar. Buradaki sorun, kilometrenizin değişebileceği açıkça görülüyorsa, birim testlerinin çoğu zaman kod kütlesini korumanın maliyetini ödeyemeyecek kadar küçük olması. En alttaki bir örnek yazdım, bir göz atın.

  2. Bu bileşen için birim testine eşdeğer oldukları iddiaları kullanın . İddialar, herhangi bir hata ayıklama yapısı boyunca her zaman doğrulandıkları hoş bir özelliğe sahiptir . Bu nedenle, "Çalışan" sınıf kısıtlamalarını ayrı bir test biriminde test etmek yerine, Çalışan sınıfını sistemdeki her sınav durumu boyunca etkin bir şekilde test ediyorsunuz . İddialar aynı zamanda kod kütlesini birim testler kadar artırmadıkları (nihayetinde iskele / alay / neyse) güzel bir özelliğe sahiptir.

    Biri beni öldürmeden önce: üretim inşası iddialara çarpmamalı. Bunun yerine "Hata" seviyesinde giriş yapmalılar.

    Henüz düşünmemiş birine dikkat olarak , kullanıcı veya ağ girişi hakkında hiçbir şey iddia etmeyin. Bu çok büyük bir hata ™.

    En son kod temellerimde, iddialar için bariz bir fırsat bulduğum her yerde, birim testlerini dikkatli bir şekilde yapıyordum. Bu, genel olarak bakım maliyetini önemli ölçüde düşürdü ve beni çok daha mutlu bir insan yaptı.

  3. Tüm birincil akışlarınız ve kullanıcı deneyimleriniz için uygulayarak sistem / entegrasyon testlerini tercih edin. Köşe durumlarda muhtemelen burada olması gerekmez. Bir sistem testi, tüm bileşenleri çalıştırarak kullanıcı sonundaki davranışı doğrular. Bu nedenle, bir sistem testi mutlaka daha yavaştır, bu yüzden önemli olanları yazın (en az değil) ve en önemli problemleri yakalayın. Sistem testlerinde genel bakım giderleri çok düşüktür.

    İddiaları kullandığınız için, her sistem testinin aynı anda birkaç yüz "birim testi" yapacağını hatırlamak önemlidir. Ayrıca en önemlilerinin birden fazla kez çalıştırıldığından emin olmalısınız.

  4. İşlevsel olarak test edilebilen güçlü API'ler yazınız. İşlevsel testler gariptir ve (bununla yüzleşelim), eğer API'nız kendi kendine çalışan bileşenleri doğrulamayı çok zorlaştırırsa anlamsızdır. İyi API tasarımı a) test adımlarını basitleştirir ve b) açık ve değerli iddialar ortaya çıkarır.

    İşlevsel testler, en çok yapılması gereken şeydir, özellikle de bir çok kişiye ya da (hatta daha da kötüsü, tanrıya) süreç engellerini aşacak kadar çok insanla iletişim kuran bileşenler varken. Tek bir bileşene ne kadar çok girdi ve çıktı bağlanırsa, zor olan işlevsellik testi, işlevselliklerini gerçekten test etmek için bunlardan birini izole etmeniz gerekir.


"Birim testlerini yazma" konusunda bir örnek vereceğim:

TEST(exception_thrown_on_null)
{
    InternalDataStructureType sink;
    ASSERT_THROWS(sink.consumeFrom(NULL), std::logic_error);
    try {
        sink.consumeFrom(NULL);
    } catch (const std::logic_error& e) {
        ASSERT(e.what() == "You must not pass NULL as a parameter!");
    }
}

Bu testin yazar katkıda bulunmayan yedi satırları ekledi hiç nihai ürünün doğrulamasına. Kullanıcı bunu asla görmemeli , çünkü a) hiç kimse NULL’u oraya NULL’dan geçirmemeli (bir iddia yazın, sonra) veya b) NULL durumu farklı davranışlara neden olmalıdır. Durumda (b) ise, bu davranışı gerçekten doğrulayan bir test yazın.

Felsefem, uygulama eserlerini test etmememiz gerektiği haline geldi. Sadece gerçek çıktı olarak düşünülebilecek her şeyi test etmeliyiz. Aksi takdirde, birim testleri (belirli bir uygulamayı zorlayan) ve uygulamanın kendisi arasında temel kod kütlesinin iki katını yazmaktan kaçınmanın bir yolu yoktur.

Burada, ünite testleri için iyi adaylar olduğunu belirtmek önemlidir. Aslında, bir ünite testinin bir şeyi doğrulamanın ve bu testleri yazmanın ve sürdürmenin değeri yüksek olduğu tek yeterli araç olduğu birkaç durum bile vardır. Başımın en üstünde, bu liste önemsiz algoritmalar, bir API'de veri konteynerlerini ve "karmaşık" görünen yüksek düzeyde optimize edilmiş kod içeriyor (aka "sonraki adam muhtemelen berbat edecek.").

Size özel tavsiyem, o zaman: Ünite testlerini silmeye başlayınca, sınava girerken, "Bu bir çıktı mı, yoksa kodumu mu harcıyorum?" Muhtemelen zamanınızı boşa harcayan şeylerin sayısını azaltmada başarılı olacaksınız.


3
Sistem / entegrasyon testlerini tercih edin - Bu zihinle uyuşmaz bir şekilde kötü. Sisteminiz, bir birim düzeyinde hızlı bir şekilde yakalanabilecek şeyleri test etmek için bu (yavaş!
Ritch Melton

1
@RitchMelton Tartışmadan tamamen ayrı, yeni bir CI sunucusuna ihtiyacınız var gibi görünüyor. CI böyle davranmamalı.
Andres Jaan Tack,

1
Bir çökme programı (iddiaların yaptığı şey) test koşucunuzu (CI) öldürmemelidir. Bu yüzden bir deneme koşucunuz var; böylece bir şey bu tür hataları tespit edip rapor edebilir.
Andres Jaan Tack,

1
Yalnızca hata ayıklama olan 'Assert' tarzı bildiğim iddialar (test iddiaları değil) geliştirici etkileşimi için beklediği için CI'yi askıda tutan bir iletişim kutusu açar.
Ritch Melton

1
Ah, bu anlaşmazlığımız hakkında çok şey açıklar. :) C tarzı iddialara atıfta bulunuyorum. Sadece şimdi bunun bir .NET sorusu olduğunu fark ettim. cplusplus.com/reference/clibrary/cassert/assert
Andres Jaan Tack

5

Bana ünite testi bir cazibe gibi çalışıyor gibi görünüyor. Bu bir olduğunu iyi bu tür bütün noktanın beri o değişikliklere kadar kırılgan olduğunu şey. Kod kırma testlerinde yapılan küçük değişiklikler sayesinde programınızdaki hata olasılığını ortadan kaldırabilirsiniz.

Ancak, yalnızca yönteminizin başarısız olmasına ya da beklenmeyen sonuçlar vermesine neden olacak koşullar için test yapmanız gerektiğini unutmayın. Bu, ünite testinizin önemsiz şeyler yerine gerçek bir sorun olduğunda "kırılmaya" meyillidir.

Bana öyle geliyor ki, programı çok fazla yeniden tasarlıyorsunuz. Bu gibi durumlarda, eski testlerde yapmanız gerekenleri yapın ve kaldırın ve daha sonra yenileriyle değiştirin. Ünite testlerinin onarımı, yalnızca programınızdaki radikal değişiklikler nedeniyle tamir etmiyorsanız faydalı olacaktır. Aksi takdirde, yeni yazılmış program kodu bölümünüzde geçerli olacak şekilde testleri yeniden yazmak için çok fazla zaman ayırdığınızı fark edebilirsiniz.


3

Başkalarının daha fazla katkısı olacağına eminim, ama benim deneyimime göre, bunlar size yardımcı olacak bazı önemli şeylerdir:

  1. Giriş veri yapıları oluşturmak için bir test nesnesi fabrikası kullanın, bu nedenle o mantığı çoğaltmanıza gerek kalmaz. Test kurulumu için gerekli kodu azaltmak için AutoFixture gibi bir yardımcı kitaplığa bakabilirsiniz .
  2. Her test sınıfı için, SUT'un oluşturulmasını merkezileştirin, böylece işler yeniden düzenlendiğinde değiştirmek kolaylaşacaktır.
  3. Unutmayın, bu test kodu üretim kodu kadar önemlidir. Eğer kod tekrarlanamazsa, vb.

Testler arasında kodu ne kadar çok kullanırsanız, o kadar kırılgan hale gelirler, çünkü şimdi bir testi değiştirmek diğerini kırabilir. Sürdürülebilirlik karşılığında bu makul bir maliyet olabilir - Ben bu tartışmaya giremiyorum - fakat 1. ve 2. noktaların testleri daha az kırılgan hale getirdiğini (ki bu sorunun yanlış olduğunu) iddia etmek.
pdr 21:11

@driis - Doğru, test kodunun kod çalıştırmadan farklı deyimleri var. 'Ortak' kodu yeniden düzenleyerek ve IoC kapları gibi şeyleri kullanarak şeyleri gizlemek, testlerinizin maruz kaldığı tasarım sorunlarını gizler.
Ritch Melton

Pdr'nin noktası ünite testleri için muhtemelen geçerli olsa da, entegrasyon / sistem testleri için "X görevine başvuruyu hazırlama" açısından düşünmenin faydalı olabileceğini savunuyorum. Bu, uygun bir yere gitmeyi, belli çalışma zamanı ayarlarını yapmayı, bir veri dosyasını açmayı vb. İçerebilir. Birden fazla entegrasyon testi aynı yerde başlarsa , böyle bir yaklaşımın risklerini ve sınırlamalarını anlarsanız, bu kodu birden fazla testte tekrar kullanmak üzere yeniden düzenlemek kötü bir şey olmayabilir .
bir CVn

2

Kaynak koduyla yaptığınız gibi testleri uygulayın.

Sürüm kontrolü, kontrol noktası bültenleri, sorun takibi, "özellik sahipliği", planlama ve çaba tahmini vb. Orada bulunmuştum - bence bu sizin tarif ettiğiniz gibi sorunlarla başa çıkmanın en etkili yolu.


1

Gerard Meszaros'un XUnit test modellerine kesinlikle bir göz atmalısınız . Test kodunuzu tekrar kullanmak ve çoğaltmayı önlemek için birçok tarif içeren harika bir bölüme sahiptir.

Eğer testleriniz kırılgansa, çiftleri test etmek için yeterince başvuruda bulunmadığınızdan da olabilir. Özellikle, her birim testinin başında nesnelerin tüm grafiklerini yeniden yaratırsanız, testlerinizdeki Bölümleri düzenle bölümleri aşırı boyutta olabilir ve çoğu zaman kendinizi, yalnızca, çünkü bölümleri önemli sayıda testte yeniden yazmak zorunda kaldığınız durumlarda bulabilirsiniz. En sık kullandığınız sınıflardan biri değişti. Alaylar ve taslaklar, burada ilgili bir test bağlamına sahip olmak için rehidre etmeniz gereken nesnelerin sayısını azaltarak size yardımcı olabilir.

Önemsiz ayrıntıların alay ve taslaklar yoluyla test kurulumlarınızdan çıkarılması ve yeniden kodlamak için test desenlerinin uygulanması kırılganlıklarını önemli ölçüde azaltmalıdır.

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.