it-swarm.asia

Bir ünite testinin bağımlılıkları test etmemesi (neden) önemlidir?

Otomatik testin değerini anlıyorum ve iyi test senaryoları ile karşılaşabileceğim sorunun yeterince iyi belirtildiği yerlerde kullanıyorum. Yine de, buradaki ve StackOverflow'daki bazı kişilerin bağımlılıklarını değil, yalnızca bir birimi vurguladığını fark ettim. Burada yararı göremiyorum.

Test bağımlılıklarından kaçınmak için alay etme/saplama, testlere karmaşıklık katar. Alaycılığı desteklemek için üretim kodunuza yapay esneklik/ayırma gereksinimleri ekler. (Bunun iyi bir tasarımı teşvik ettiğini söyleyen herkese katılmıyorum. Ekstra kod yazmak, bağımlılık enjeksiyon çerçeveleri gibi şeyleri tanıtmak veya başka bir şeyi gerçek bir kullanım durumu olmadan daha esnek/takılabilir/genişletilebilir/ayrıştırılmış yapmak için kod tabanınıza karmaşıklık eklemek, iyi tasarım.)

İkincisi, test bağımlılıkları, her yerde kullanılan kritik düşük seviyeli kodun, testlerini açıkça yazanların dışındaki girdilerle test edildiği anlamına gelir. Bağlı olduğu düşük düzeyli işlevselliği alay etmeden yüksek düzeyli işlevsellik üzerinde birim testleri çalıştırarak düşük düzeyli işlevsellikte birçok hata buldum. İdeal olarak bunlar, düşük seviyeli işlevsellik için birim testleri tarafından bulunmuş olabilir, ancak kaçırılan durumlar her zaman gerçekleşir.

Bunun diğer tarafı nedir? Birim testinin bağımlılıklarını da test etmemesi gerçekten önemli mi? Öyleyse neden?

Düzenleme: Veritabanları, ağlar, web hizmetleri, vb gibi alay dış bağımlılıklar değerini anlayabiliyorum (Anna Lear sayesinde bana bunu açıklamak için motive .) iç bağımlılıklara , yani herhangi bir doğrudan dış bağımlılığa sahip olmayan diğer sınıflara, statik işlevlere vb.

107
dsimcha

Bu bir tanım meselesi. Bağımlılık içeren bir test, birim test değil, bir entegrasyon testidir. Ayrıca bir entegrasyon test takımınız olmalıdır. Fark, entegrasyon test paketinin farklı bir test çerçevesinde çalıştırılabilmesidir ve muhtemelen daha uzun sürdüğü için derlemenin bir parçası olamaz.

Ürünümüz için: Birim testlerimiz her derleme ile saniyeler içinde yapılır. Entegrasyon testlerimizin bir alt kümesi, her check-in ile 10 dakika sürmektedir. Tam entegrasyon paketimiz her gece 4 saat sürmektedir.

118
Jeffrey Faust

Mevcut tüm bağımlılıklarla test etmek hala önemlidir, ancak Jeffrey Faust'un söylediği gibi entegrasyon testi alanında daha fazladır.

Birim testinin en önemli yönlerinden biri, testlerinizi güvenilir kılmaktır. Başarılı bir testin gerçekten işlerin iyi olduğu ve başarısız bir testin gerçekten üretim kodunda bir sorun olduğu anlamına gelmezse, testleriniz olabildiğince yararlı değildir.

Testlerinizi güvenilir kılmak için birkaç şey yapmanız gerekiyor, ancak bu cevap için sadece bir tanesine odaklanacağım. Çalıştırmanın kolay olduğundan emin olmalısınız, böylece tüm geliştiriciler kodu kontrol etmeden önce bunları kolayca çalıştırabilirler. "Çalışması kolay", testlerinizin hızlı bir şekilde çalışması anlamına gelir ve Kapsamlı bir yapılandırma veya onları kurmak için kurulum gerekiyordu. İdeal olarak, kodun en son sürümünü kontrol edebilmeli, testleri hemen çalıştırabilmeli ve başarılı olduklarını görebilmelidir.

Diğer şeylere (dosya sistemi, veritabanı, web hizmetleri, vb.) Bağımlılıkların soyutlanması, yapılandırma gerektirmekten kaçınmanızı sağlar ve sizi ve diğer geliştiricileri, "Oh, testler yapmadım çünkü test yapamadım" Ağ paylaşımını ayarlamayın. Oh iyi. Onları daha sonra çalıştıracağım. "

Bazı verilerle ne yaptığınızı test etmek istiyorsanız, o iş mantığı kodu için birim testleriniz nasıl bu verileri aldığınız umurumda olmamalıdır. Veritabanları gibi destekleyicilere bağlı kalmadan uygulamanızın temel mantığını test edebilmek harika. Eğer yapmıyorsanız, kaçırıyorsunuz demektir.

Not; Ek olarak, test edilebilirlik adına aşırı mühendisliğin mümkün olduğunu da eklemeliyim. Uygulamanızı test etmek bunu hafifletmeye yardımcı olur. Ancak her durumda, bir yaklaşımın zayıf uygulamaları yaklaşımı daha az geçerli kılmaz. "Neden bunu yapıyorum?" Diye sormaya devam etmezse, herhangi bir şey yanlış kullanılabilir ve abartılabilir. geliştirirken.


public class MyClass 
{
    private SomeClass someClass;
    public MyClass()
    {
        someClass = new SomeClass();
    }

    // use someClass in some way
}

Genellikle SomeClass 'nin nasıl oluşturulduğu umurumda değil. Sadece kullanmak istiyorum. SomeClass değişir ve şimdi yapıcı için parametreler gerektiriyorsa ... bu benim sorunum değil. Bunu karşılamak için MyClass'ı değiştirmem gerekmemelidir.

Şimdi, bu sadece tasarım kısmına dokunuyor. Birim testleri söz konusu olduğunda kendimi diğer sınıflardan da korumak istiyorum. MyClass'ı test ediyorsanız, dış bağımlılık olmadığını, SomeClass'ın bir noktada bir veritabanı bağlantısı veya başka bir harici bağlantı tanıtmadığını bilmek hoşuma gidiyor.

Ancak daha da büyük bir sorun, bazı yöntemlerimin sonuçlarının SomeClass'taki bazı yöntemlerin çıktısına dayandığını da biliyorum. SomeClass ile alay etmeden/saplamadan, talep edilen girdiyi değiştirmenin bir yolu olmayabilir. Şanslıysam, testimin içindeki ortamımı SomeClass'ın doğru yanıtını tetikleyecek şekilde oluşturabilirim, ancak bu şekilde devam etmek testlerime karmaşıklık getirir ve onları kırılgan hale getirir.

MyClass'ı yapıcıda SomeClass örneğini kabul etmek için yeniden yazmak, istediğim değeri (sahte bir çerçeve veya manuel bir sahte) döndüren sahte bir SomeClass örneği oluşturmamı sağlar. Ben genellikle bu durumda bir arayüz tanıtmak için have yok. Bunun yapılıp yapılmayacağı, birçok yönden tercih ettiğiniz dile göre belirlenebilecek kişisel bir seçimdir (örneğin, arayüzler C #'da daha olasıdır, ancak kesinlikle Ruby'de bir taneye ihtiyacınız yoktur).

39
Adam Lear

Ünite ile entegrasyon testi sorunu dışında aşağıdakileri göz önünde bulundurun.

Sınıf Widget Thingamajig ve WhatsIt sınıflarına bağımlılıkları vardır.

Widget için birim testi başarısız.

Sorun hangi sınıfta yatıyor?

"Hata ayıklayıcıyı çalıştır" veya "kodu bulana kadar oku" yanıtını verdiyseniz, bağımlılıkları değil yalnızca birimi test etmenin önemini anlarsınız.

22
Brook

Programlamanın yemek pişirmek gibi olduğunu düşünün. Daha sonra birim testi, malzemelerinizin taze, lezzetli, vb. Olmasını sağlamakla aynıdır.

Sonuçta, yemeğinizin lezzetli olduğundan (veya sisteminizin çalıştığından) emin olmak en önemli şey, nihai hedeftir. Ama eğer malzemeleriniz, yani, birimler, iş, öğrenmek için daha ucuz bir yolunuz olacak.

Gerçekten de, birimlerinizin/yöntemlerinizin çalışacağını garanti edebiliyorsanız, çalışan bir sisteminiz olması daha olasıdır. "Kesin" yerine "daha muhtemel" i vurguluyorum. Yine de, pişirdiğiniz yemeği tatmak ve son ürünün iyi olduğunu söylemek için birine ihtiyaç duyduğunuz gibi entegrasyon testlerine ihtiyacınız var. Taze malzemelerle oraya gitmek daha kolay olacak, hepsi bu.

15
Julio

Üniteleri tamamen izole ederek test etmek, TÜM olası veri varyasyonlarının ve bu ünitenin gönderilebileceği durumların test edilmesine izin verecektir. Diğerlerinden ayrıldığından, test edilecek ünite üzerinde doğrudan etkisi olmayan varyasyonları göz ardı edebilirsiniz. bu da testlerin karmaşıklığını büyük ölçüde azaltacaktır.

Entegre çeşitli bağımlılık düzeyleriyle test yapmak, birim testlerinde test edilmemiş olabilecek belirli senaryoları test etmenizi sağlar.

Her ikisi de önemlidir. Sadece birim testi yaparak, bileşenleri entegre ederken meydana gelen her zamankinden daha karmaşık ince bir hata elde edersiniz. Sadece entegrasyon testi yapmak, makinenin her bir parçasının test edilmediğinden emin olmadan bir sistemi test ettiğiniz anlamına gelir. Entegrasyon testi yaparak daha iyi bir tam test yapabileceğinizi düşünmek, giriş kombinasyonunun sayısını ne kadar fazla bileşen eklediğinizde ÇOK hızlı (faktöriyel) düşünün ve yeterli kapsama sahip bir test oluşturmak çok hızlı bir şekilde imkansız hale geldiğinden neredeyse imkansızdır.

Kısacası, üç seviyeli "birim testi" genellikle neredeyse tüm projelerimde kullanıyorum:

  • Tek bir bileşenin cehennemini test etmek için alaycı veya stubbed bağımlılıklarla izole edilen birim testleri. İdeal olarak tam kapsamlı bir girişimde bulunulur.
  • Daha ince hataları test etmek için entegrasyon testleri. Sınır durumları ve tipik koşulları gösterecek özenle hazırlanmış nokta kontrolleri. Tam kapsama alanı çoğu zaman mümkün değildir, sistemin neyi başarısız yapmasına odaklanan çaba.
  • Sistemde çeşitli gerçek yaşam verilerini enjekte etmek, verileri (mümkünse) enstrümanlamak ve spesifikasyona ve iş kurallarına uyduğundan emin olmak için çıktıyı gözlemlemek için şartname testi.

Bağımlılık enjeksiyonu, sisteme karmaşıklık eklemeden birim testleri için bileşenleri çok kolay bir şekilde izole etmeyi sağladığı için bunu başarmanın çok etkili bir yoludur. İş senaryosunuz, enjeksiyon mekanizması kullanımını garanti etmeyebilir, ancak test senaryosunuz neredeyse olacaktır. Bu benim için vazgeçilmez olmak için yeterli. Bunları, kısmi entegrasyon testi ile farklı soyutlama seviyelerini bağımsız olarak test etmek için de kullanabilirsiniz.

6
Newtopian

Bunun diğer tarafı nedir? Birim testinin bağımlılıklarını da test etmemesi gerçekten önemli mi? Öyleyse neden?

Birimi. Tekil anlamına gelir.

2 şeyi test etmek, şu iki işleve sahip olduğunuz anlamına gelir ve tüm fonksiyonel bağımlılıklar.

3. bir şey eklerseniz, testlerdeki fonksiyonel bağımlılıkları doğrusal olarak artırırsınız. Şeyler arasındaki ara bağlantılar şey sayısından daha hızlı büyür.

Test edilen n öğe arasında n (n-1)/2 potansiyel bağımlılık vardır.

Bu büyük sebep.

Sadeliğin değeri vardır.

4
S.Lott

Özyineleme yapmayı ilk öğrendiğinizi hatırlıyor musunuz? Prof prof. "X yapan bir yönteminiz olduğunu varsayın" dedi (örn. Herhangi bir x için fibbonacci çözer). "X için çözmek için bu yöntemi x-1 ve x-2 için çağırmalısınız". Aynı bakımdan, bağımlılıkları yok etmek onların var olduğunu iddia etmenizi ve mevcut ünitenin yapması gerekeni yaptığını test etmenizi sağlar. Varsayım, elbette, bağımlılıkları titizlikle test ettiğinizdir.

Bu aslında işyerinde SRP'dir. Testleriniz için bile tek bir sorumluluğa odaklanmak, yapmanız gereken zihinsel hokkabazlık miktarını ortadan kaldırır.

2
Michael Brown

(Bu küçük bir yanıttır. İpucu için teşekkürler @TimWilliscroft.)

Aşağıdaki durumlarda hataların yerelleştirilmesi daha kolaydır:

  • Hem test altındaki birim hem de bağımlılıklarının her biri bağımsız olarak test edilir.
  • Her test, yalnızca testin kapsadığı kodda bir arıza varsa ve başarısız olursa.

Bu kağıt üzerinde güzel çalışıyor. Bununla birlikte, OP'nin açıklamasında gösterildiği gibi (bağımlılıklar hatalıdır), bağımlılıklar test edilmiyorsa, hatanın yerini belirlemek zor olacaktır.

2
rwong

Çok iyi cevaplar. Ayrıca birkaç nokta daha eklerdim:

Birim testi, bağımlılıklar yokken kodunuzu test etmenizi sağlar. Örneğin. siz veya ekibiniz henüz diğer katmanları yazmadınız veya belki başka bir şirket tarafından sunulan bir arabirimi bekliyorsunuz.

Birim testleri ayrıca geliştirici makinenizde tam bir ortamınızın olması gerekmediği anlamına gelir (örneğin bir veritabanı, bir web sunucusu vb.). Tüm geliştiricilerin do böyle bir ortama sahip olmasını şiddetle tavsiye ederim, ancak hataları vb. test etmek, daha büyük bir test sistemine girmeden önce kodunuza en azından bir miktar güven verir.

2
Steve

Ah ... burada bu cevaplarda yazılmış ünite ve entegrasyon testinde iyi noktalar var!

Burada maliyetle ilgili ve pratik görünümler özledim. Çok izole edilmiş/atomik birim testlerinin (belki de birbirinden oldukça bağımsız ve bunları paralel olarak ve veritabanları, dosya sistemi vb. Gibi herhangi bir bağımlılık olmadan çalıştırma seçeneği ile) ve (daha yüksek seviye) entegrasyon testlerinin faydasını açıkça gördüğümü söyledi. ama ... aynı zamanda bir maliyetler (zaman, para, ...) ve riskler meselesi.

Bu yüzden deneyimlerimden çok daha önemli olan başka faktörler de var ("ne test edilir") "nasıl test edilir".

Müşterim (sezgisel olarak) fazladan yazma ve bakım testi için ödeme yapıyor mu? Daha test odaklı bir yaklaşım (kodu yazmadan önce testler yaz) çevremde gerçekten uygun maliyetli mi (kod hatası riski/maliyet analizi, insanlar, tasarım özellikleri, test ortamını ayarlama)? Kod her zaman hatalıdır, ancak testi üretim kullanımına taşımak daha uygun maliyetli olabilir mi (en kötü durumda!)?

Ayrıca kod kalitenizin (standartların) ne olduğuna ya da sizin ve ekibinizin izlediği çerçevelere, IDE'ye, tasarım ilkelerine vb. Çok bağlıdır. İyi yazılmış, kolayca anlaşılabilir, yeterince iyi belgelenmiş ( ideal olarak kendi kendini belgeleme), modüler, ... kodu, tam tersine göre daha az hata ortaya çıkarır. Bu nedenle, gerçek "ihtiyaç", baskı veya genel bakım/hata düzeltme maliyetleri/kapsamlı testler için riskler yüksek olmayabilir.

Ekibimdeki bir iş arkadaşının önerdiği uç noktaya götürelim, saf Java EE model katman kodumuzu tüm sınıflar için% 100 kapsama alanı ile birim test etmeye çalışmalıyız ve Veya entegrasyon testlerinin tüm olası gerçek dünya kullanım durumlarının ve web kullanıcı arabirimi iş akışlarının% 100'ünü kapsamasını isteyen yönetici, çünkü herhangi bir kullanım durumunu başarısızlığa uğratmak istemiyoruz. Ancak sıkı bir bütçemiz var yaklaşık 1 milyon avro, her şeyi kodlamak için oldukça sıkı bir plan ... Potansiyel uygulama hatalarının insanlar veya şirketler için çok büyük bir tehlike oluşturmayacağı bir müşteri ortamı. Uygulamamız dahili olarak (bazı) önemli birim testleri, entegrasyon testleri ile test edilecek , tasarlanmış test planları, bir test aşaması vb ile anahtar müşteri testleri. Bazı nükleer fabrika veya ilaç üretimi için bir uygulama geliştirmiyoruz! (Sadece bir belirlenmiş veritabanı, her geliştirici için klonu test etmek kolay ve webapp ile sıkıca bağlı veya model katmanı)

Kendimi mümkünse test yazmaya ve kodumu birim test ederken geliştirmeye çalışıyorum. Ancak bunu genellikle yukarıdan aşağıya bir yaklaşımdan (entegrasyon testi) yapıyorum ve önemli testler için (genellikle model katmanında) "uygulama katmanı kesilmesi" için iyi bir nokta bulmaya çalışıyorum. (çünkü genellikle "katmanlar" ile ilgili çok şey vardır)

Ayrıca, ünite ve entegrasyon test kodu zaman, para, bakım, kodlama vb. Üzerinde olumsuz bir etki yaratmaz. Serindir ve uygulanmalıdır, ancak 5 yıllık testin başlangıcından veya 5 yıl sonra başlarken veya sonrasında dikkatle düşünülmelidir kodu.

Bu yüzden gerçekten söyleyebilirim güven ve maliyet/risk/fayda değerlendirmesine çok bağlıdır ... gerçek hayatta olduğu gibi, çok fazla veya 100 ile dolaşmak istemiyorsunuz % güvenlik mekanizmaları.

Çocuk bir yere tırmanabilir ve tırmanmalıdır ve düşebilir ve kendine zarar verebilir. Yanlış yakıt doldurduğum için araç çalışmayı durdurabilir (geçersiz giriş :)). Tost 3 yıl sonra çalışmayı durdurursa kızarmış ekmek yanabilir. Ama asla, otoyolda sürmek ve müstakil direksiyonumu ellerimde tutmak istemiyorum :)

0
Andreas Dietrich

Tasarım yönleri hakkında: Küçük projelerin bile kodu test edilebilir hale getirmekten yararlandığına inanıyorum. Guice (basit bir fabrika sınıfı genellikle yapacak) gibi bir şey tanıtmak zorunda değilsiniz, ancak inşaat sürecini programlama mantığından ayırır.

  • her sınıfın bağımlılıklarını arabirimi aracılığıyla açıkça belgelemek (takımda yeni olan insanlar için çok yararlı)
  • sınıfların bakımı daha net ve kolay hale gelir (çirkin nesne grafiği oluşturma işlemini ayrı bir sınıfa koyduğunuzda)
  • gevşek bağlantı (değişiklikleri çok daha kolay hale getirir)
0
Oliver Weiler