Yararlılığa dayalı birim test türleri


13

Değer açısından benim pratiğimde iki grup birim test görüyorum:

  1. Önemsiz mantığı test eden testler. Bunları (uygulamadan önce veya sonra) yazmak bazı sorunları / potansiyel hataları ortaya çıkarır ve gelecekte mantığın değişmesi durumunda emin olmanıza yardımcı olur.
  2. Bazı önemsiz mantığı test eden testler. Bu testler, test etmekten çok belge kodu (tipik olarak alaylarla) gibidir. Bu testlerin bakım iş akışı "bazı mantık değişti, test kırmızı oldu - Tanrı bu testi yazdım, teşekkürler" ama "bazı önemsiz kod değişti, test yanlış negatif oldu - testi herhangi bir kazanç elde etmeden sürdürmeliyim (yeniden yazıyorum)" . Çoğu zaman bu testler sürdürülmeye değmez (dini nedenler hariç). Ve birçok sistemdeki tecrübelerime göre, bu testler tüm testlerin% 80'i gibidir.

Diğer adamların birim testlerin değerlere göre ayrılması ve benim ayrılıma nasıl karşılık geldiği konusunda ne düşündüğünü bulmaya çalışıyorum. Ama en çok gördüğüm şey ya tam zamanlı TDD propagandası ya da testler-faydasız-sadece-kod-yaz propagandası. Ortada bir şeyle ilgileniyorum. Kendi düşünceleriniz veya makalelere / makalelere / kitaplara referanslarınız açıktır.


3
Ünite testlerini, bir zamanlar orijinal ünite testleri setinden geçtiği bilinen (spesifik) hataları kontrol etmek için rolü regresyon hatalarını önlemek olan ayrı bir grup olarak kontrol ediyorum.
Konrad Morawski

6
Bu ikinci tür testler, bir tür "değişim sürtünmesi" olarak gördüğüm testlerdir. Yararlılıklarını azaltmayın. Kodun önemsizliklerini bile değiştirmek, kod tabanı boyunca dalgalanma etkilerine sahip olma eğilimindedir ve bu tür sürtünmeyi tanıtmak, geliştiricilerinize engel teşkil eder, böylece bazı tuhaf veya kişisel tercihlere dayanmak yerine sadece gerçekten ihtiyaç duydukları şeyleri değiştirirler .
Telastyn

3
@Telastyn - Yorumunuzla ilgili her şey benim için tamamen deli görünüyor. Kim kasten kod değiştirmeyi zorlaştırabilir? Neden geliştiricileri uygun gördükleri gibi kod değiştirmekten vazgeçiyorlar - onlara güvenmiyor musunuz? Kötü geliştiriciler mi?
Benjamin Hodgson

2
Her durumda, kodun değiştirilmesi "dalgalanma efektlerine" sahip olma eğiliminde ise, kodunuzun bir tasarım sorunu vardır - bu durumda devs makul olduğu kadar yeniden düzenleyici olmaya teşvik edilmelidir. Kırılgan testler, yeniden düzenlemeyi aktif olarak caydırır (bir test başarısız olur; bu testin gerçekten hiçbir şey yapmayan testlerin% 80'inden biri olup olmadığını anlamaktan kim rahatsız olabilir? Bunu yapmak için farklı, daha karmaşık bir yol bulursunuz). Ama bunu arzu edilen bir özellik olarak görüyorsunuz ... Hiç anlamıyorum.
Benjamin Hodgson

2
Her neyse, OP bu blog gönderisini Rails'in yaratıcısından ilginç bulabilir. Amacını aşırı derecede basitleştirmek için, muhtemelen testlerin% 80'ini atmaya çalışmalısınız.
Benjamin Hodgson

Yanıtlar:


14

Ünite testlerinde bir bölünmeyle karşılaşmanın doğal olduğunu düşünüyorum. Düzgün nasıl yapılacağı konusunda birçok farklı görüş vardır ve doğal olarak diğer tüm görüşler doğal olarak yanlıştır . Son zamanlarda DrDobbs hakkında cevabımın sonunda bağlantı verdiğim bu konuyu araştıran birkaç makale var.

Testlerde gördüğüm ilk sorun, onları yanlış anlamanın kolay olmasıdır. Üniversite C ++ dersimde hem birinci hem de ikinci yarıyılda birim sınavlara maruz kaldık. Her iki dönemde de genel olarak programlama hakkında hiçbir şey bilmiyorduk - C ++ ile programlamanın temellerini öğrenmeye çalışıyorduk. Şimdi öğrencilere "Oh hey, sen biraz yıllık vergi hesap makinesi yazdın! Şimdi doğru çalıştığından emin olmak için bazı birim testleri yaz" dediğini hayal et. Sonuçlar açık olmalı - girişimlerim dahil hepsi korkunçtu.

Birim testleri yazmayı kabul ettiğinizi ve daha iyisini elde etmek istediğinizi kabul ettikten sonra, kısa bir süre içinde modaya uygun test stilleri veya farklı metodolojilerle karşılaşacaksınız. Metodolojileri test ederek, önce test veya DrDobbs'dan Andrew Binstock'un ne yaptığına, testleri kodun yanında yazan uygulamalara atıfta bulunuyorum. Her ikisinin de artıları ve eksileri var ve ben herhangi bir sübjektif ayrıntıya girmeyi reddediyorum çünkü bu bir alev savaşını kışkırtacak. Hangi programlama metodolojisinin daha iyi olduğu konusunda şaşkın değilseniz, belki de test tarzı işe yarayacaktır. TDD, BDD, Mülk tabanlı test mi kullanmalısınız? JUnit, TDD ve Mülk tabanlı test arasındaki çizgiyi bulanıklaştıran Teoriler adı verilen gelişmiş kavramlara sahiptir. Ne zaman kullanılır?

tl; dr Testi yanlış yapmak kolaydır, inanılmaz derecede fikirlidir ve herhangi bir test yönteminin, uygun oldukları bağlamda özenle ve profesyonelce kullanıldığı sürece doğal olarak daha iyi olduğuna inanmıyorum. bence kalkınmaya hızlı ve geçici bir yaklaşım sağlamak için kullanılan iddialara veya akıl sağlığı testlerinin bir uzantısı.

Sübjektif bir görüş için, daha iyi bir ifade eksikliği için testlerin "aşamalarını" yazmayı tercih ederim. Sınıfları ayrı ayrı test eden ve gerektiğinde alaylarla test eden birim testleri yazıyorum. Bunlar muhtemelen JUnit veya benzeri bir şeyle yürütülecektir. Daha sonra entegrasyon veya kabul testleri yazıyorum, bunlar ayrı olarak ve genellikle günde sadece birkaç kez yapılır. Bunlar önemsiz olmayan kullanım durumunuz. Genellikle BDD'yi, JUnit'in kolayca sağlayamayacağı bir şey olan doğal dilde ifade etmek güzel olduğu için kullanıyorum.

Son olarak, kaynaklar. Bunlar, çoğunlukla farklı dillerde ve farklı çerçevelerle birim testi etrafında odaklanan çelişkili görüşler sunacaktır. Senin fikrini çok fazla manipüle etmediğim sürece kendi fikrini oluşturmana izin verirken ideoloji ve metodolojideki bölünmeyi sunmalılar :)

[1] Çevikliğin Bozulması Andrew Binstock

[2] Önceki makalenin yanıtlarına yanıt

[3] Bob Amca tarafından Çevikliğin Yolsuzluğuna Yanıt

[4] Rob Myers'ın Çevikliğinin Yolsuzluğuna Cevabı

[5] Salatalık Testi Neden Rahatsız Edilir?

[6] Yanlış söylüyorsun

[7] Araçlardan Uzaklaşın

[8] 'Romen Rakamlarıyla Kata Yorumlu' yorum

[9] Yorumlu Romen Rakamları Kata


1
Dostça görüşlerimden biri, yıllık vergi hesaplayıcısının işlevini test etmek için bir test yazıyorsanız, bir birim testi yazmamanızdır. Bu bir entegrasyon testi. Hesap makineniz oldukça basit yürütme birimlerine bölünmelidir ve birim testleriniz bu birimleri test eder. Bu birimlerden biri düzgün çalışmayı durdurursa (test başarısız olur), o zaman temel duvarın bir kısmını çalmak gibidir ve kodu onarmanız gerekir (genellikle testi değil). Ya bu, ya da artık gerekli olmayan ve atılması gereken bir kod belirlediniz.
Craig

1
@Craig: Kesinlikle! Doğru testleri nasıl yazacağımı bilmemekle kastediyorum. Bir üniversite öğrencisi olarak, vergi tahsildarı, SOLID'i doğru anlamadan yazılmış büyük bir sınıftır. Bunun her şeyden daha fazla bir entegrasyon testi olduğunu düşünmek kesinlikle doğrudur, ancak bu bizim için bilinmeyen bir terimdir. Profesörümüz tarafından sadece "birim" testlerine maruz kaldık.
IAE

5

Her iki tipte de testler yapmanın ve gerektiğinde kullanmanın önemli olduğuna inanıyorum.

Dediğin gibi, iki uç var ve dürüst olmak gerekirse ikisinden biri ile aynı fikirde değilim.

Anahtar, birim testlerin iş kurallarını ve gereksinimlerini kapsaması gerektiğidir . Sistemin bir kişinin yaşını izlemesi gerektiği şartı varsa, yaşın negatif olmayan bir tamsayı olduğundan emin olmak için "önemsiz" testler yazın. Sistem için gerekli olan veri alanını test ediyorsunuz: önemsiz olsa da , sistemin parametrelerini zorladığı için değeri vardır .

Aynı şekilde daha karmaşık testlerle de değer getirmek zorundalar. Elbette, şart olmayan ancak bir yerde bir fildişi kulesinde uygulanması gereken bir şeyi doğrulayan bir test yazabilirsiniz, ancak müşterinin size ödediği gereksinimleri doğrulayan testler yazmak için daha iyi zaman harcanan testler yazabilirsiniz. Örneğin, kodunuzu doğrulayan bir test neden yalnızca akışlar ağdan değil, yerel dosyalardan olduğunda zaman aşımına uğrayan bir giriş akışı ile ilgilenebilir?

Birim testlerine inanıyorum ve TDD'yi mantıklı olan her yerde kullanıyorum. Birim testleri, kod değiştirilirken kesinlikle yüksek kalite ve "hızlı başarısız" davranışı şeklinde değer getirir. Ancak, akılda tutulması gereken eski 80/20 kuralı da vardır. Bir noktada, testler yazarken azalan getirilere ulaşacaksınız ve daha fazla test yazmanın ölçülebilir bir değeri olsa bile daha üretken bir çalışmaya geçmeniz gerekiyor.


Bir sistemin bir kişinin yaşını izlediğinden emin olmak için bir test yazmak bir birim test değildir, IMO. Bu bir entegrasyon testi. Bir birim testi, örneğin, herhangi bir birimdeki (günler, haftalar, vb.) Bir temel tarih ve bir ofsetten bir yaş değeri hesaplayan genel yürütme birimini ("prosedür" olarak da bilinir) test eder. Demek istediğim, kod biraz sistemin geri kalanında garip giden bağımlılıklar olmamalıdır. SADECE birkaç giriş değerinden bir yaşı hesaplar ve bu durumda birim testi doğru davranışı onaylayabilir, bu da ofset negatif bir yaş üretiyorsa büyük olasılıkla bir istisna atmaktır.
Craig

Herhangi bir hesaplamaya değinmedim. Bir model bir parça veri depolarsa, verilerin doğru alana ait olduğunu doğrulayabilir. Bu durumda, etki alanı negatif olmayan tamsayılar kümesidir. Hesaplamalar kontrolörde (MVC'de) yapılmalıdır ve bu örnekte yaş hesaplaması ayrı bir test olacaktır.

4

İşte benim almam: tüm testlerin maliyeti var:

  • başlangıç ​​zamanı ve çabası:
    • neyi test edeceğinizi ve nasıl test edeceğinizi düşünün
    • testi uygulayın ve ne yapması gerektiğini test ettiğinden emin olun
  • Devam eden bakım
    • kod doğal olarak geliştikçe testin hala yapılması gerekeni yaptığından emin olmak
  • testi çalıştırmak
    • uygulama vakti
    • sonuçları analiz etmek

Ayrıca, tüm testlerin fayda sağlamayı amaçlıyoruz (ve deneyimlerime göre, neredeyse tüm testler fayda sağlıyor):

  • Şartname
  • vurgu köşe kılıfları
  • gerilemeyi önlemek
  • otomatik doğrulama
  • API kullanımına örnekler
  • spesifik özelliklerin ölçümü (zaman, mekan)

Bu yüzden, eğer bir sürü test yazarsanız, muhtemelen bir değere sahip olacaklarını görmek oldukça kolaydır. Bunun karmaşık hale geldiği yerde, bu değeri (bu arada, önceden bilmiyor olabilirsiniz - kodunuzu atıyorsanız, regresyon testleri değerlerini kaybeder) maliyetle karşılaştırmaya başladığınız zamandır.

Artık zamanınız ve çabanız sınırlıdır. En yüksek faydayı en düşük maliyetle sağlayan şeyleri yapmak istersiniz. Ve bunun çok zor bir şey olduğunu düşünüyorum, en azından bir kişinin sahip olmaması ya da elde edilmesi pahalı olacağı bilgisini gerektirebileceğinden.

Ve bu farklı yaklaşımlar arasındaki gerçek sürtünme. Hepsinin faydalı test stratejileri belirlediğine inanıyorum. Ancak, her stratejinin genel olarak farklı maliyetleri ve faydaları vardır. Ayrıca, her bir stratejinin maliyeti ve faydaları büyük olasılıkla projenin, alanın ve ekibin özelliklerine bağlı olacaktır. Başka bir deyişle, en iyi birden çok yanıt olabilir.

Bazı durumlarda, kodu testsiz olarak pompalamak en iyi faydaları / maliyetleri sağlayabilir. Diğer durumlarda, kapsamlı bir test takımı daha iyi olabilir. Yine de diğer durumlarda, tasarımı geliştirmek en iyi şey olabilir.


2

Ne olduğunu bir birim gerçekten test? Burada gerçekten büyük bir ikilik var mı?

Tam anlamıyla bir arabellek sonuna kadar bir bit okumanın bir programı tamamen çökertebileceği veya tamamen yanlış bir sonuç üretmesine neden olabileceği veya son zamanlarda yapılan "HeartBleed" TLS hatasıyla kanıtlandığı gibi, güvenli bir sistem ortaya koyduğu bir alanda çalışıyoruz. kusurun doğrudan bir kanıtı olmadan açıktır.

Bu sistemlerden tüm karmaşıklığı ortadan kaldırmak imkansızdır. Ancak işimiz, mümkün olduğu ölçüde, bu karmaşıklığı en aza indirmek ve yönetmek.

Bir ünite testi, örneğin, üç farklı sistemde rezervasyonun başarıyla gönderildiğini, bir günlük girişi oluşturulduğunu ve bir E-posta onayı gönderildiğini doğrulayan bir test midir?

Ben diyeceğim yok . Bu bir entegrasyon testi. Ve kesinlikle onların yeri var, ama aynı zamanda farklı bir konu.

Entegrasyon testi, tüm "özelliklerin" genel işlevini doğrulamak için çalışır. Ancak bu özelliğin arkasındaki kod, basit, test edilebilir yapı taşlarına, yani "birimlere" ayrılmalıdır.

Bu yüzden, bir birim testin kapsamı çok sınırlı olmalıdır.

Bu , birim test tarafından test edilen kodun çok sınırlı bir kapsama sahip olması gerektiği anlamına gelir .

Bu ayrıca, iyi tasarımın sütunlarından birinin, karmaşık sorununuzu birbirinden göreceli olarak ayrı ayrı test edilebilen daha küçük, tek amaçlı parçalara (mümkün olduğu ölçüde) bölmek olduğunu ima eder.

Sonuçta, güvenilir temel bileşenlerinden oluşan bir sistem var ve bu temel birim birimlerinden herhangi birinin kesilip kesilmediğini biliyorsunuz çünkü tam olarak bunu söylemek için basit, küçük, sınırlı kapsamlı testler yazdınız.

Çoğu durumda, muhtemelen birim başına birden fazla testiniz olmalıdır. Testlerin kendileri basit olmalı ve mümkün olduğu kadar sadece bir davranışı test etmelidir.

Önemsiz, ayrıntılı, karmaşık mantığı test eden bir "birim test" kavramı, sanırım biraz oksimoron.

Kasıtlı tasarım ayrışması sonunda bu tür gerçekleştikten Yani eğer, o zaman nasıl dünyada bir birim test aniden yanlış pozitif üretme başlayabileceğini sürece test kod biriminin temel fonksiyonu değişti? Ve eğer bu olduysa, oyunda belirgin olmayan dalgalanma etkileri olduğuna inanmanız daha iyi olur. Yanlış pozitif üretiyor gibi görünen kırık testiniz aslında bazı değişikliklerin kod tabanında daha geniş bir bağımlılık çemberini kırdığı ve incelenmesi ve düzeltilmesi gerektiği konusunda sizi uyarıyor.

Bu birimlerin (birçoğunun) sahte nesneler kullanılarak test edilmesi gerekebilir, ancak bu daha karmaşık veya ayrıntılı testler yazmanız gerektiği anlamına gelmez.

Rezervasyon sistemiyle ilgili tartışmalı örneğime geri dönersek , kodunuzu her birim test ettiğinizde canlı bir rezervasyon veritabanına veya üçüncü taraf hizmetine (hatta "dev" örneğine) istek gönderemezsiniz .

Yani aynı arayüz sözleşmesini sunan alayları kullanın. Testler daha sonra nispeten küçük, deterministik bir kod yığınının davranışını doğrulayabilir. Tüm tahtadaki yeşiller size temelinizi oluşturan blokların kırılmadığını söyler .

Ancak, bireysel ünite testlerinin mantığı mümkün olduğunca basit kalır.


1

Bu elbette, sadece benim düşüncem, ama son birkaç ay fsharp (bir C # arka planından geliyor) fonksiyonel programlama öğrenmek geçirdim birkaç şey fark etmemi sağladı.

OP'nin belirttiği gibi, her gün gördüğümüz tipik olarak 2 tip "birim testi" vardır. Genellikle en değerli olan, ancak "algoritmalar" hakkında daha az ve "soyutlamalar" hakkında daha fazla olan sistemin% 80'i için zor olan bir yöntemin giriş ve çıkışlarını kapsayan testler.

Diğer tip, tipik olarak alaycılığı içeren soyutlama etkileşimini test etmektir. Benim düşünceme göre, bu testler çoğunlukla uygulamanızın tasarımı nedeniyle gereklidir. Onları ommiting ve garip böcekleri ve spagetti kodunu riske atarsınız, çünkü insanlar önce testleri yapmak zorunda kalmadıkları sürece tasarımlarını düzgün düşünmezler (ve hatta genellikle karıştırırlar). Sorun, test metodolojisi değil, sistemin altında yatan tasarımdır. Zorunlu veya OO dilleri ile oluşturulan sistemlerin çoğu "yan etkiler" olarak da bilinir, "Bunu yapın, ama bana hiçbir şey söyleme". Yan etkiye güvendiğinizde, test etmeniz gerekir, çünkü bir iş gereksinimi veya işlemi genellikle bunun bir parçasıdır.

Sisteminizi daha etkili bir şekilde tasarladığınızda, yan etkilere bağımlılıklar oluşturmamanız ve durum değişikliklerinden / değişmezlikten izlemekten kaçınmanız, daha fazla eylemi açıkça test eden "giriş ve çıkış" testlerine daha fazla odaklanmanızı sağlar. ve oraya nasıl ulaşacağınız daha az. Değişmezlik gibi şeylerin size aynı sorunlara çok daha basit çözümler açısından neler yapabileceğine şaşıracaksınız ve artık "yan etkilere" bağımlı olmadığınızda paralel ve eşzamansız programlama gibi şeyleri neredeyse hiçbir ek ücret ödemeden yapabilirsiniz.

Fsharp'ta kodlamaya başladığımdan beri, hiçbir şey için alaycı bir çerçeveye ihtiyaç duymadım ve hatta bağımlılığımı bir IOC Container'a döktüm. Testlerim iş gereksinimi ve değerine dayanıyor ve zorunlu programlamada kompozisyon elde etmek için genellikle gerekli olan ağır soyutlama katmanlarında değil.

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.