Özel yöntemleri sınama hakkında bu yazı okudum. Genelde onları test etmiyorum, çünkü her zaman nesnenin dışından çağrılacak olan genel yöntemleri test etmenin daha hızlı olacağını düşündüm. Özel yöntemleri test ediyor musunuz? Onları her zaman test etmeli miyim?
Özel yöntemleri birim testi yapmam. Özel bir yöntem, sınıfın kullanıcılarına gizlenmesi gereken bir uygulama detayıdır. Özel yöntemlerin test edilmesi, kapsüllemeyi keser.
Özel yöntemin kendi testlerini gerektirecek kadar büyük ya da karmaşık ya da önemli olduğunu tespit edersem, başka bir sınıfa koyar ve onu halka açık tutarım ( Yöntem Nesne ). O zaman şimdi kendi sınıfında yaşayan önceden-özel-ama-şimdi-kamu yöntemini kolayca test edebilirim.
Testin amacı nedir?
Şimdiye kadar verilen cevapların çoğunluğu, özel yöntemlerin, kamuoyu arayüzü iyi bir şekilde test edildiği ve çalıştığı sürece önemli olmayan (veya hiç olmaması gereken) uygulama detayları olduğunu söylüyor. Bu kesinlikle doğru eğer test için tek amacınız ortak arayüzün çalışacağını garanti etmekse.
Şahsen, kod sınamalarında birincil kullanımım gelecekteki kod değişikliklerinin sorun çıkarmamasını sağlamak ve varsa hata ayıklama çabalarıma yardımcı olmaktır. Özel yöntemleri test etmenin kamu arayüzü (tamamen olmasa bile) bu amaç için daha ayrıntılı olduğunu buluyorum.
Şunu düşünün: Özel yöntem B'yi çağıran genel yöntem A'ya sahipsiniz. Hem A hem de B, yöntem C'yi kullanıyorlar. C, A'nın testlerini geçemesine neden olarak değiştirildi. Sorunun A için C, B'nin C kullanımı veya her ikisinin de kullanımında olup olmadığını bilmek için, özel olsa bile, B için test yaptırmanız faydalı olmaz mıydı?
Özel yöntemlerin test edilmesi, kamu arabiriminin test kapsamının eksik olduğu durumlarda da değer katar. Bu genellikle kaçınmak istediğimiz bir durum olsa da, verimlilik birimi testi hem hataları bulma testlerine hem de bu testlerin ilişkili geliştirme ve bakım maliyetlerine bağlıdır. Bazı durumlarda,% 100 test kapsamının yararlarının, bu arayüzlerin test kapsamında boşluklar yaratarak, bu testlerin maliyetlerini garanti altına almak için yetersiz olduğu düşünülebilir. Bu gibi durumlarda, özel bir yöntemin iyi hedeflenmiş bir testi kod tabanına çok etkili bir ekleme olabilir.
Dave Thomas ve Andy Hunt'ın tavsiyelerini kitabında takip etme eğilimindeyim Pragmatik Birim Testi :
Genel olarak, .__ uğruna herhangi bir enkapsülasyonu kırmak istemezsiniz. test (ya da annemin dediği gibi, "mahremiyetinizi açığa vurmayın!"). Çoğu zamanın bir sınıfını sınava tabi tutmalısınız. kamu yöntemleri. Gizli bir işlevsellik varsa gizlidir. Özel veya korumalı erişimin arkasında, bu bir uyarı işareti olabilir. dışarı çıkmak için mücadele eden başka bir sınıf var.
Ancak bazen kendimi özel yöntemler denemekten alıkoyamam çünkü bu bana tamamen sağlam bir program oluşturduğuma dair güvence duygusu veriyor.
Projemizdeki en son KG önerisinden birini ve daha fazlasını takip ettiğim için özel fonksiyonları test etmeye mecbur olduğumu hissediyorum:
İşlev başına 10'dan fazla giriş döngüsel karmaşıklık .
Şimdi bu politikanın uygulanmasının yan etkisi, çok büyük kamu işlevlerimin çoğunun daha iyi odaklanmış, daha iyi private işlevine bölünmüş olmasıdır.
Kamu işlevi hala orada (elbette) fakat temel olarak tüm bu özel 'alt işlevler' olarak adlandırılıyor.
Bu gerçekten harika, çünkü çağrı dizisinin okunması artık çok daha kolay (büyük bir işlevin içindeki bir hata yerine, alt dizgede bir hata var, bu çağrılarımdaki önceki işlevlerin adını anlıyorum. 'oraya nasıl geldim')
Ancak, doğrudan şu private işlevlerini birim sınaması yapmak ve büyük kamu işlevinin sınamasını, bir senaryonun ele alınması gereken bir tür 'entegrasyon' testine bırakmak artık daha kolay görünüyor.
Sadece 2 sentim.
Evet, özel fonksiyonları test ediyorum, çünkü kamuya açık yöntemleriniz tarafından test edilmelerine rağmen, uygulamanın en küçük kısmını test etmek TDD'de (Test Odaklı Tasarım) Güzel. Ancak, test birimi sınıfındayken özel işlevlere erişilemez. Özel yöntemlerimizi test etmek için yaptığımız şey.
Neden özel yöntemlerimiz var?
Özel fonksiyonlar genel olarak sınıfımızda mevcuttur çünkü genel yöntemlerimizde okunabilir kodlar oluşturmak istiyoruz .. __ Bu sınıfın kullanıcısının bu yöntemleri doğrudan çağırmasını istemiyoruz, ama genel yöntemlerimiz aracılığıyla. Ayrıca, sınıfı (korunma durumunda) genişletirken davranışlarını değiştirmek istemiyoruz, bu yüzden özel.
Kodlarken, test odaklı tasarım (TDD) kullanıyoruz. Bu, bazen özel ve test etmek isteyen bir fonksiyonelliğe rastlamamız anlamına gelir. Özel işlevler phpUnit'te test edilebilir değildir, çünkü Test sınıfında bunlara erişemiyoruz (özeldirler).
Burada 3 çözüm olduğunu düşünüyoruz:
1. Özel yöntemlerinizi ortak yöntemlerle test edebilirsiniz
Avantajlar
Dezavantajları
2. Eğer özel çok önemliyse, belki de bunun için yeni bir sınıf oluşturmak bir kodlamadır
Avantajlar
Dezavantajları
3. Erişim değiştiriciyi (final) korumalı olarak değiştirin
Avantajlar
Dezavantajları
Örnek
class Detective { public function investigate() {} private function sleepWithSuspect($suspect) {} } Altered version: class Detective { public function investigate() {} final protected function sleepWithSuspect($suspect) {} } In Test class: class Mock_Detective extends Detective { public test_sleepWithSuspect($suspect) { //this is now accessible, but still not overridable! $this->sleepWithSuspect($suspect); } }
Bu nedenle test birimimiz şimdi eski özel fonksiyonumuzu test etmek için test_sleepWithSuspect'i çağırabilir.
Birkaç nedenden dolayı özel işlevselliği test etmeyi sevmiyorum. Bunlar aşağıdaki gibidir (bunlar TLDR halkının ana noktalarıdır):
Bunların her birini somut bir örnekle açıklayacağım. 2) ve 3) 'ün biraz karmaşık bir şekilde bağlantılı olduğu ortaya çıktı, bu nedenle örnekleri benzer, ancak özel yöntemleri test etmemeniz için ayrı nedenler olduğunu düşünüyorum.
Özel yöntemlerin uygun olduğunu test eden zamanlar vardır, yukarıda listelenen olumsuzlukların farkında olmanız önemlidir. Daha sonra daha ayrıntılı olarak inceleyeceğim.
TDD'nin neden özel yöntemleri test etmek için geçerli bir bahane olmadığını da anladım.
Gördüğüm en yaygın (anti) paterlerden biri Michael Feathers ne diyor "Iceberg" sınıfı (Michael Feathers'ın kim olduğunu bilmiyorsanız, "Eski Kod ile Etkili Çalışmak" adlı kitabını satın almak/okumak için gidin. Profesyonel bir yazılım mühendisi/geliştiricisi olup olmadığınızı bilmeye değer bir kişidir. ). Bu sorunun artmasına neden olan başka (anti) modeller de var, ancak bu, karşılaştığım en yaygın olanı. "Buzdağı" sınıflarının ortak bir yöntemi vardır ve geri kalanı özeldir (bu yüzden özel yöntemleri test etmek caziptir). Buna "Buzdağı" sınıfı denir, çünkü genellikle baştan çıkarmaya çalışan yalnız bir yöntem vardır, ancak işlevselliğin geri kalanı özel yöntemler biçiminde su altında gizlenir. Bu gibi bir şey görünebilir:
Örneğin, art arda bir dizgide çağırarak ve beklenen sonucu verdiğini görerek
GetNextToken() test etmek isteyebilirsiniz. Bunun gibi bir işlev bir sınama gerektirir: bu davranış önemsiz değildir, özellikle de belirteç kurallarınız karmaşıksa. Tüm bu o kadar da karmaşık değil gibi davranalım ve sadece uzayla sınırlanmış belirteçleri iple çekmek istiyoruz. Yani bir test yazıyorsunuz, belki de şuna benziyor (bazı dillerde agnostik psuedo kodu, umarım fikir açıktır):
TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens) { input_string = "1 2 test bar" re = RuleEvaluator(input_string); ASSERT re.GetNextToken() IS "1"; ASSERT re.GetNextToken() IS "2"; ASSERT re.GetNextToken() IS "test"; ASSERT re.GetNextToken() IS "bar"; ASSERT re.HasMoreTokens() IS FALSE; }
Aslında bu bayağı hoş gözüküyor. Değişiklik yaparken bu davranışı sürdürdüğümüzden emin olmak isteriz. Fakat
GetNextToken() private işlevidir! Dolayısıyla, böyle test edemiyoruz, çünkü derleme yapmayacak bile (Python gibi bazı betik dillerinin aksine, genel/özel olarak zorlayan bir dil kullanıyoruz). Ancak, Tek Sorumluluk İlkesini (Tek Sorumluluk İlkesi) izleyerek
RuleEvaluator sınıfını değiştirmeye ne dersiniz? Örneğin, bir sınıflayıcıda sıkışmış bir ayrıştırıcı, belirteç ve değerlendirici var gibi görünüyor. Bu sorumlulukları sadece ayırmak daha iyi olmaz mıydı? Bunun üzerine, eğer bir
Tokenizer sınıfı yaratırsanız, o zaman genel yöntemler
HasMoreTokens() ve
GetNextTokens() olacaktır.
RuleEvaluator sınıfı üye olarak
Tokenizer nesnesine sahip olabilir. Şimdi, yukarıdakiyle aynı testi uygulayabiliriz, ancak
Tokenizer sınıfı yerine
RuleEvaluator sınıfını test ediyoruz.
İşte UML'de göründüğü gibi:
Bu yeni tasarımın modülerliği arttırdığına dikkat edin, bu nedenle bu sınıfları sisteminizin diğer bölümlerinde yeniden kullanabilirsiniz (yapamadan önce, özel yöntemler tanım olarak yeniden kullanılamaz). Bu, RuleEvaluator'ı düşürmenin ve anlaşılabilirliğin/yerellik artışının beraberinde getirdiği ana avantajdır.
Test, son derece benzer gözükecekti, çünkü bu kez
GetNextToken() yöntemi şimdi
Tokenizer sınıfında herkese açık olduğundan derlendi.
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens) { input_string = "1 2 test bar" tokenizer = Tokenizer(input_string); ASSERT tokenizer.GetNextToken() IS "1"; ASSERT tokenizer.GetNextToken() IS "2"; ASSERT tokenizer.GetNextToken() IS "test"; ASSERT tokenizer.GetNextToken() IS "bar"; ASSERT tokenizer.HasMoreTokens() IS FALSE; }
Sorununuzu daha az modüler bileşene ayırabileceğinizi düşünmese bile (eğer sadece yapmaya çalışırsanız zamanın% 95'ini yapabilirsiniz), basitçe kamuya açık bir arayüz aracılığıyla özel fonksiyonları test edin. Çoğu zaman özel üyeler test etmeye değmez çünkü ortak arayüz aracılığıyla test edilirler. Gördüğüm birçok kez çok benzeyen testler benzer, ancak iki farklı işlevi/yöntemi test eder. Sonuçta ortaya çıkan şey şu ki, ihtiyaçlar değiştiğinde (ve her zaman yaparlarsa), şimdi 1 yerine 2 kırık testiniz var. Ve eğer tüm özel yöntemlerinizi gerçekten test ettiyseniz, 1 yerine 10 kırık testinden daha fazlasına sahip olabilirsiniz Kısacası, genel bir arabirim aracılığıyla sınanabilecek özel işlevleri sınama (
FRIEND_TEST kullanarak veya bunları genel veya yansıtma kullanarak) sınama yinelemesine neden olabilir . Bunu gerçekten istemiyorsun, çünkü hiçbir şey test odasından seni yavaşlatan daha fazla acıtmıyor. Geliştirme süresini ve bakım maliyetlerini düşürmesi gerekiyordu! Genel bir arabirim aracılığıyla test edilen özel yöntemleri test ederseniz, test paketi bunun tersini yapabilir ve bakım maliyetlerini etkin bir şekilde artırabilir ve geliştirme süresini artırabilir. Özel bir işlevi herkese açık yaptığınız zaman veya
FRIEND_TEST ve/veya yansıma gibi bir şey kullanırsanız, genellikle uzun vadede pişmanlık duyarsınız.
Tokenizer sınıfının aşağıdaki olası uygulamasını göz önünde bulundurun:
Diyelim ki
SplitUpByDelimiter(), dizideki her öğenin bir belirteç olacağı bir diziyi döndürmekten sorumludur. Dahası, diyelim ki
GetNextToken() sadece bu vektör üzerinde bir yineleyicidir. Yani açık testiniz şöyle görünebilir:
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens) { input_string = "1 2 test bar" tokenizer = Tokenizer(input_string); ASSERT tokenizer.GetNextToken() IS "1"; ASSERT tokenizer.GetNextToken() IS "2"; ASSERT tokenizer.GetNextToken() IS "test"; ASSERT tokenizer.GetNextToken() IS "bar"; ASSERT tokenizer.HasMoreTokens() IS false; }
Michael Feather'ın bir groping aracı olarak adlandırdıklarını varsayalım. Bu, diğer kişilerin özel bölümlerine dokunmanıza olanak sağlayan bir araçtır. Bir örnek googletest'ten
FRIEND_TEST veya dil destekliyorsa yansımadır.
TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens) { input_string = "1 2 test bar" tokenizer = Tokenizer(input_string); result_array = tokenizer.SplitUpByDelimiter(" "); ASSERT result.size() IS 4; ASSERT result[0] IS "1"; ASSERT result[1] IS "2"; ASSERT result[2] IS "test"; ASSERT result[3] IS "bar"; }
Pekala, şimdi şartların değiştiğini ve belirtecin çok daha karmaşık hale geldiğini varsayalım. Basit bir dize sınırlayıcısının yeterli olmayacağına karar verirsiniz ve işi yürütmek için
Delimiter sınıfına ihtiyacınız vardır. Doğal olarak, bir testin kırılmasını beklersiniz, ancak özel fonksiyonları test ederken bu acı artar.
Yazılımda "tek beden herkese uyar" yoktur. Bazen "kuralları çiğnemek" iyidir (ve aslında idealdir). Yapabildiğiniz zaman özel işlevleri test etmemeyi şiddetle savunuyorum. Tamam olduğunu düşündüğümde iki ana durum var:
Eski sistemler ile yoğun bir şekilde çalıştım (bu yüzden bu kadar büyük bir Michael Feathers hayranıyım) ve bazen sadece özel işlevleri test etmenin en güvenli olduğunu söyleyebilirim. Temel olarak "karakterizasyon testleri" almak için özellikle yararlı olabilir.
Acele içindesiniz ve burada ve şimdi için mümkün olan en hızlı şeyi yapmak zorundasınız. Uzun vadede, özel yöntemleri test etmek istemezsiniz. Ancak, tasarım konularına değinmek için genellikle refactor uygulamasının biraz zaman alacağını söyleyeceğim. Bazen de bir hafta içinde gemi olmalısın. Sorun değil: hızlı ve kirli yapın ve işinizi yapmanın en hızlı ve en güvenilir yolu olduğunu düşünüyorsanız, bir groping aracı kullanarak özel yöntemleri test edin. Fakat yaptığınız şeyin uzun vadede yetersiz kaldığını anlayın ve lütfen ona geri dönmeyi düşünün (ya da unutulmuşsa, ancak daha sonra görürseniz düzeltin).
Tamam olması muhtemel başka durumlar da olabilir. Tamam olduğunu düşünüyorsanız ve iyi bir gerekçeniz varsa, o zaman yapın. Kimse seni durduramıyor. Sadece potansiyel maliyetlerin farkında ol.
Bir yana, TDD'yi özel yöntemleri test etmek için bahane olarak kullanan insanları gerçekten sevmiyorum. TDD'yi uyguluyorum ve TDD kuvvetlerini sanmıyorum Bunu yapman için İlk önce testinizi (genel arayüzünüz için) yazabilir ve ardından bu arayüzü karşılamak için kod yazabilirsiniz. Bazen genel bir arayüz için bir test yazarım ve bunu bir veya iki küçük özel yöntemi de yazarak tatmin ederim (ancak özel yöntemleri doğrudan test etmiyorum, ancak çalıştığını veya genel testimin başarısız olacağını biliyorum. ). Eğer bu özel yöntemin Edge vakalarını test etmem gerekirse, halka açık ara yüzüm üzerinden onlara ulaşacak bir sürü test yazacağım. Edge kasalarına nasıl ulaşacağınızı bulamıyorsanız, bu her biri kendi genel yöntemleriyle küçük parçalara yeniden yerleştirmeniz için güçlü bir işarettir. Bu, özel işlevlerin çok fazla yaptığınız ve sınıfın kapsamı dışında olduğu bir işareti .
Ayrıca, bazen şu anda çiğnemek için çok fazla ısırıktan büyük bir test yazdığımı anlıyorum ve bu yüzden "eh, çalışmak için daha fazla API’m olduğunda daha sonra tekrar o teste döneceğim" (I Bunu yorumlayacağım ve aklımın arkasına saklayacağım). Burası, karşılaştığım birçok devin daha sonra günah keçisi olarak TDD kullanarak özel işlevsellik için testler yazmaya başlayacağı yer. "Ah, iyi, başka bir teste ihtiyacım var, ama bu testi yazmak için, bu özel yöntemlere ihtiyacım olacak. Bu nedenle, bir test yazmadan herhangi bir üretim kodu yazamadığım için bir test yazmam gerekiyor. özel bir yöntem için. " Ancak gerçekten yapmaları gereken şey, mevcut sınıflarına bir grup özel yöntemi eklemek/test etmek yerine daha küçük ve tekrar kullanılabilir bileşenlere yeniden yapılanmaktır.
Not:
Özel yöntemleri GoogleTest kullanarak test etme hakkında kısa bir süre önce benzer bir soruyu cevapladım. Bu cevabı çoğunlukla burada daha fazla dil agnostik olacak şekilde değiştirdim.
Not; İşte Michael Feathers'ın buzdağı sınıfları ve el yordamıyla ilgili araçlarla ilgili ders: https://www.youtube.com/watch?v=4cVZvoFGJT
Bir nesnenin genel arayüzünü test etmenin en iyisi olduğunu düşünüyorum. Dış dünya bakış açısına göre, sadece kamusal arabirimin davranışı önemlidir ve ünite testleriniz buna yönelik olmalıdır.
Bir nesne için yazılmış bazı katı birim testlerine sahip olduğunuzda, sadece arabirimin arkasındaki uygulama değiştiği için geri dönmek ve bu testleri değiştirmek istemezsiniz. Bu durumda, birim testinizin tutarlılığını bozmuş olursunuz.
Özel yöntem iyi tanımlanmışsa (yani, test edilebilir ve zaman içinde değişmesi amaçlanmayan bir işlevi vardır), o zaman evet. Mantıklı olduğu her yerde test edilebilir olan her şeyi test ediyorum.
Örneğin, bir şifreleme kütüphanesi, aynı anda yalnızca 8 bayt şifreleyen özel bir yöntemle blok şifreleme gerçekleştirdiği gerçeğini gizleyebilir. Bunun için bir birim testi yazacaktım - gizlenmiş olsa bile değişmek istemedi ve eğer kırılırsa (örneğin gelecekteki performans artışları nedeniyle), o zaman sadece kırılan özel fonksiyon olduğunu bilmek istiyorum. kamusal işlevlerden birinin kırdığını söyledi.
Daha sonra hata ayıklamayı hızlandırır.
-Adam
Özel yönteminiz kamuya açık yöntemlerinizi arayarak test edilmezse, o zaman ne yapıyor?
Test odaklı (TDD) geliştiriyorsanız, özel yöntemlerinizi test edeceksiniz.
Bu alanda uzman değilim, ancak birim sınaması davranışı test etmeli, uygulamayı sınamalıdır. Özel yöntemler kesinlikle uygulamanın bir parçasıdır, bu yüzden IMHO test edilmemelidir.
Özel yöntemleri çıkarsama ile test ediyoruz, bunun anlamı en az% 95'lik toplam sınıf sınavı kapsamını arıyoruz, ancak testlerimiz genel veya dahili yöntemlere yöneliktir. Kapsamı elde etmek için, meydana gelebilecek farklı senaryolara dayanarak kamuya/iç bölgelere birden fazla çağrı yapmamız gerekiyor. Bu, testlerimizi test ettikleri kodun amacı doğrultusunda daha bilinçli hale getirir.
Trumpi'nin linklendiğin göreve cevabı en iyisi.
Birim testleri, genel yöntemleri test etmek için olduğuna inanıyorum. Genel yöntemleriniz özel yöntemlerinizi kullanır, dolaylı olarak da test edilirler.
Bir süredir özellikle TDD'de elimi denemekle uğraşıyorum.
TDD'de bu sorunu yeterince iyi ele aldığımı düşündüğüm iki gönderiyle karşılaştım.
Özetle:
Teste dayalı geliştirme (tasarım) tekniklerini kullanırken, özel yöntemler yalnızca zaten çalışan ve test edilen kodun yeniden faktoring işlemi sırasında ortaya çıkmalıdır.
Sürecin doğası gereği, tamamen test edilmiş bir işlevden çıkarılan herhangi bir basit uygulama işlevselliği biraz kendi kendine test edilecektir (yani dolaylı test kapsamı).
Bana göre, kodlamanın başlangıç bölümünde çoğu yöntemin daha yüksek seviyeli fonksiyonlar olacağı açıktır, çünkü tasarımı kapsıyor/tarif ediyorlar.
Bu nedenle, bu yöntemler halka açık olacak ve test etmek yeterince kolay olacaktır.
Her şey yolunda gittiğinde özel yöntemler daha sonra ortaya çıkacak ve okunabilirlik ve temizlik uğruna faktoring yapıyoruz.
Yukarıda belirtildiği gibi, "Özel yöntemlerinizi test etmezseniz, kırılmayacaklarını nereden biliyorsunuz?"
Bu önemli bir konudur. Birim testlerinin en büyük noktalarından biri, ASAP'ı nerede, ne zaman ve nasıl kırdığını bilmek. Böylece önemli miktarda geliştirme ve KG çabasını azaltır. Test edilenlerin tümü halk ise, o zaman dürüst bir şekilde kapsama almazsınız ve sınıfın içindekileri tanımlamazsınız.
Bunu yapmanın en iyi yollarından birini projeye test referansı eklemek ve testleri özel yöntemlere paralel bir sınıfa koymak olduğunu gördüm. Testlerin son projeye dahil edilmemesi için uygun yapı mantığını kullanın.
Daha sonra bu yöntemlerin test edilmesinin tüm avantajlarına sahip olursunuz ve dakikalar veya saatler gibi saniyeler içinde problemleri bulabilirsiniz.
Özetle, evet, birim özel yöntemlerinizi test eder.
Yapmamalısın . Özel yöntemleriniz test edilmesi gereken yeterli karmaşıklığa sahipse, bunları başka bir sınıfa koymalısınız. Tutun yüksek uyum , bir sınıfın tek bir amacı olmalıdır. Sınıfın genel arayüzü yeterli olmalı.
Özel yöntemlerinizi test etmezseniz, kırılmadıklarını nereden biliyorsunuz?
Belli ki dile bağlı. Geçmişte c ++ ile test sınıfını arkadaş sınıfı olarak ilan ettim. Ne yazık ki bu, üretim kodunuzun test sınıfı hakkında bilgi sahibi olmasını gerektirir.
Özel yöntemlerin uygulama detayları olarak kabul edildiği ve daha sonra test edilmeleri gerekmediği fikrini anlıyorum. Ve sadece nesnenin dışında gelişmek zorunda olsaydık bu kurala sadık kaldım. Fakat biz, biz sadece nesnelerin dışında gelişen, sadece kamusal yöntemlerini çağıran bir tür sınırlı geliştiriciler miyiz? Yoksa aslında o nesneyi mi geliştiriyoruz? Dış nesneleri programlamak zorunda olmadığımız için, muhtemelen bu özel yöntemleri geliştirmekte olduğumuz yeni halka açık yöntemlere çağırmamız gerekecek. Özel yöntemin tüm ihtimallere karşı direnç gösterdiğini bilmek harika olmaz mıydı?
Bazı insanların bu nesneye başka bir kamu yöntemi geliştirirsek, o zaman bunun test edilmesi gerektiğini ve (özel yöntem testsiz yaşamaya devam edebileceğini) cevaplayabileceğini biliyorum. Ancak bu, bir nesnenin genel yöntemleri için de geçerlidir: bir web uygulaması geliştirilirken, bir nesnenin genel yöntemlerinin tümü denetleyiciler yöntemlerinden çağrılır ve bu nedenle denetleyiciler için uygulama ayrıntıları olarak kabul edilebilir.
Peki neden birim test nesneleri yapıyoruz? Gerçekten zor olduğu için, kontrolörün yöntemlerini, ilgili kodun tüm dallarını tetikleyecek uygun girdiyle test ettiğimizden emin olmak imkansız değil. Başka bir deyişle, yığında ne kadar yüksek olursa, tüm davranışı test etmek o kadar zor olur. Ve özel yöntemler için de aynı şey geçerli.
Bana göre özel ve kamusal yöntemler arasındaki sınır, testler söz konusu olduğunda psikolojik bir kriterdir. Benim için daha önemli olan kriterler:
Özel yöntemin kendi testlerini gerektirecek kadar büyük veya karmaşık veya önemli olduğunu tespit edersem, onu başka bir sınıfa koyar ve orada kamuya açıklarım (Yöntem Nesne). Öyleyse, daha önce özel olan, ancak artık kendi sınıfında yaşayan ortak yöntemi kolayca sınayabilirim.
"Özel yöntemleri test etmeli miyim?" Bazen". Genelde sınıflarınızın arayüzüne karşı test yapıyor olmalısınız.
İşte bir örnek:
class Thing def some_string one + two end private def one 'aaaa' end def two 'bbbb' end end class RefactoredThing def some_string one + one_a + two + two_b end private def one 'aa' end def one_a 'aa' end def two 'bb' end def two_b 'bb' end end
RefactoredThing'da şimdi 2 testin yenilenmesi için güncellemeniz gereken 5 testiniz var, ancak nesnenizin işlevselliği gerçekten değişmedi. Öyleyse, olayların bundan daha karmaşık olduğunu ve çıktının sırasını tanımlayan bir yönteminiz olduğunu varsayalım:
def some_string_positioner if some case elsif other case elsif other case elsif other case else one more case end end
Bu, harici bir kullanıcı tarafından çalıştırılmamalıdır, ancak kapsülleyici sınıfınız, bu kadar fazla mantığı tekrar tekrar çalıştırmak için ağır olabilir. Bu durumda belki bunu ayrı bir sınıfa çıkarmak, o sınıfa bir arayüz vermek ve ona karşı test etmek isteyebilirsiniz.
Son olarak, ana nesnenizin çok ağır olduğunu ve yöntemin oldukça küçük olduğunu ve çıktının doğru olduğundan emin olmanız gerektiğini varsayalım. "Bu özel yöntemi denemek zorundayım!" Diye düşünüyorsunuz. Belki ağır işlerin bir kısmını bir başlatma parametresi olarak geçirerek nesnenizi daha hafif hale getirebileceğinizi düşündünüz mü? O zaman daha hafif bir şey geçirip buna karşı test edebilirsin.
Bu sadece kamusal veya özel yöntemler veya işlevlerle değil, uygulama detaylarıyla da ilgilidir. Özel fonksiyonlar, uygulama detaylarının sadece bir yönüdür.
Sonuçta, birim testi beyaz kutu testi yaklaşımıdır. Örneğin, testlerde şu ana kadar ihmal edilen kodun bölümlerini tanımlamak için kapsama analizi kullananlar uygulama detaylarına giriyor.
A) Evet, uygulama detaylarını test ediyor olmalısınız:
Performans nedenleriyle, 10'a kadar öğe varsa özel bir BubbleSort uygulaması ve 10'dan fazla öğe varsa, farklı bir sıralama yaklaşımının (örneğin, heapsort) özel bir uygulamasını kullanan bir sıralama işlevini düşünün. Genel API, sıralama işlevine aittir. Bununla birlikte, test takımınız, aslında iki tür algoritma kullanıldığı bilgisini daha iyi kullanır.
Bu örnekte, kesinlikle, herkese açık API üzerinde testler yapabilirsiniz. Bununla birlikte, bu, sıralama işlevini 10'dan fazla elemanla yürüten bir dizi test senaryosuna sahip olmasını gerektirecektir, öyle ki, heapsort algoritması yeterince iyi bir şekilde test edilmiştir. Bu tür test durumlarının tek başına varlığı, test paketinin işlevin uygulama ayrıntılarına bağlı olduğunu gösterir.
Sıralama işlevinin uygulama ayrıntıları değişirse, belki iki sıralama algoritması arasındaki sınır değiştirilir veya bu küme kümesinin yerine mergesort veya başka bir şey konulursa: Mevcut testler çalışmaya devam eder. Bununla birlikte, değerleri yine de sorgulanabilir ve değişen sıralama işlevini daha iyi test etmek için büyük olasılıkla elden geçirilmeleri gerekir. Başka bir deyişle, testlerin herkese açık API'da olmasına rağmen, bir bakım çabası olacaktır.
B) Uygulama detayları nasıl test edilir?
Birçok insanın birinin özel fonksiyonları veya uygulama ayrıntılarını test etmemesi gerektiğini iddia etmesinin bir nedeni, uygulama ayrıntılarının değişmesinin daha muhtemel olmasıdır. Değişimin bu daha yüksek olması en azından uygulama detaylarının arayüzlerin arkasına gizlenmesinin sebeplerinden biridir.
Şimdi, arayüzün arkasındaki uygulamanın, iç arayüz üzerindeki bireysel testlerin bir seçenek olabileceği daha büyük özel parçalar içerdiğini varsayalım. Bazı insanlar, bu bölümlerin özel olduğunda test edilmemesi, halka açık bir şey haline getirilmesi gerektiğini savunuyor. Genel olarak, bu kodu birim sınaması tamam olacaktır.
Bu ilginç: Arayüz dahili iken, uygulama detayı olarak değişmesi muhtemeldi. Aynı arayüzü alarak, herkese açık hale getirerek bazı sihirli dönüşümler yapar, yani değişmesi daha az olası bir arayüze dönüşür. Açıkçası bu tartışmada bazı kusurlar var.
Bununla birlikte, bunun arkasında bazı gerçekler vardır: Uygulama detaylarını test ederken, özellikle de iç arayüzler kullanarak, kişi kararlı kalabilecek arayüzleri kullanmaya çalışmalıdır. Bununla birlikte, bazı arayüzlerin kararlı olması muhtemel olup olmadığı, genel ya da özel olup olmadığına bağlı olarak basitçe kararsız değildir. Bir süredir üzerinde çalıştığım dünyadan gelen projelerde, halka açık arayüzler de sıklıkla yeterince değişiyor ve birçok özel arayüz uzun süre dokunulmadan kaldı.
Yine de, "ilk önce ön kapıyı" kullanmak iyi bir kuraldır (bkz. http://xunitpatterns.com/Principles%20of%20Test%20Automation.html ). Ancak, "sadece ön kapı" değil, "ilk önce kapı" olarak adlandırıldığını unutmayın.
C) Özet
Ayrıca uygulama ayrıntılarını test edin. Stabil arayüzlerde test etmeyi tercih edin (halka açık veya özel). Uygulama detayları değişirse, genel API üzerindeki testlerin de revize edilmesi gerekir. Özel bir şeyi halka açmak, istikrarını sihirli bir şekilde değiştirmez.
Ayrıca yöntem paketinizi özel, yani varsayılan yapabilirsiniz ve özel olması gerekmedikçe test edebilirsiniz.
Bir ana nokta
Mantığın doğruluğunu sağlamak için test yaparsak ve özel bir yöntem de bir mantık taşıyorsa test etmeliyiz. Öyle değil mi? Peki neden bunu atlayacağız?
Yöntemlerin görünürlüğüne dayalı testler yazmak tamamen alakasız bir fikirdir.
Tersine
Öte yandan, asıl sınıfın dışında özel bir yöntem aramak temel bir sorundur. Ayrıca bazı alaycı araçlarda özel bir yöntemle alay etmek için sınırlamalar vardır. (Örn: Mockito)
Power Mock gibi bazı araçlar olsa da, bunu destekleyen tehlikeli bir işlemdir. Sebep, bunu başarmak için JVM'yi kırması gerek.
Buralarda yapılabilecek işlerden biri (Özel yöntemler için test çalışmaları yazmak istiyorsanız)
Bu private yöntemlerini korumalı olarak bildirin. Ancak, birkaç durum için uygun olmayabilir.
Eğer yöntem yeterince önemli/karmaşıksa, genellikle onu “korumalı” hale getirip test edeceğim. Bazı yöntemler özel olarak bırakılacak ve açık/korunan yöntemler için birim testlerinin bir parçası olarak örtük olarak test edilecektir.
Evet, mümkün olduğunca özel yöntemleri denemelisiniz. Niye ya? Gereksiz yere durum alanı patlaması sonuçlarından kaçınmak için, sonuçta aynı girişlerde tekrar tekrar aynı özel fonksiyonları tekrar tekrar test etmek yeterli. Neden bir örnekle açıklayalım.
Aşağıdaki biraz tartışmalı örneği düşünün. Genel olarak, 3 tam sayı alan ve sadece bu 3 tamsayının hepsinin asaleti olduğunda doğru olan bir işlevi göstermek istediğimizi varsayalım. Bunu şöyle uygulayabiliriz:
public bool allPrime(int a, int b, int c) { return andAll(isPrime(a), isPrime(b), isPrime(c)) } private bool andAll(bool... boolArray) { foreach (bool b in boolArray) { if(b == false) return false; } return true; } private bool isPrime(int x){ //Implementation to go here. Sorry if you were expecting a prime sieve. }
Şimdi, sadece kamusal işlevlerin test edilmesi gerektiği yönünde katı bir yaklaşım benimsememiz durumunda,
allPrime testini yapmamıza izin verdik ve
isPrime veya
andAll değil.
Bir test cihazı olarak, her bir argüman için beş olasılıkla ilgilenebiliriz:
< 0,
= 0,
= 1,
prime > 1,
not prime > 1. Ancak, daha ayrıntılı olmak için, argümanların her bir kombinasyonunun birlikte nasıl oynadığını görmemiz gerekir. Yani bu
5*5*5 = 125 test senaryoları sezgilerimize göre bu işlevi tamamen test etmemiz gerekecek.
Öte yandan, özel fonksiyonları test etmemize izin verilirse, daha az sayıda test vakasıyla çok fazla alanı kapsayabiliriz.
isPrime 'yi önceki sezgimizle aynı seviyede test etmek için sadece 5 test vakasına ihtiyacımız var. Ve Daniel Jackson tarafından önerilen küçük kapsam hipotezi tarafından,
andAll işlevini küçük bir boyuta kadar test etmemiz gerekir. 3 veya 4. En fazla 16 test olacak. Yani toplamda 21 test. 125 yerine, elbette, muhtemelen
allPrime üzerinde bir few testleri yapmak isterdik, ancak değer verdiğimiz 125 giriş senaryosunun tümünü ayrıntılı bir şekilde ele almak zorunda kalmayacağız. Sadece bir kaç mutlu yol.
Kesin bir örnek, kesin, ancak açık bir gösteri için gerekliydi. Ve kalıp gerçek yazılıma uzanır. Özel fonksiyonlar genellikle en düşük seviye yapı taşlarıdır ve bu nedenle genellikle daha yüksek seviye mantığı sağlamak için bir araya getirilir. Daha yüksek seviyelerde, çeşitli kombinasyonlar nedeniyle daha düşük seviyeli malzemelerin tekrarları daha fazladır.
Birçok insanın aynı düşünce tarzında olduğunu görüyorum: kamu düzeyinde test. Fakat QA ekibimizin yaptığı bu değil mi? Girdi ve beklenen çıktıyı test ederler. Eğer geliştiriciler olarak sadece kamu yöntemlerini test edersek, sadece QA'nın işini yineliyoruz ve "birim testi" ile herhangi bir değer eklemiyoruz.
Herkese açık vs özel, testlerinizden ne gibi şeylerin çağrılacağına ya da metot sınıfına karşı ne kadar faydalı olduğu konusunda yararlı bir ayrım değildir. Test edilebilir birimlerin çoğu bir bağlamda görünür, ancak diğerlerinde gizlenir.
Önemli olan kapsam ve maliyetler. Projenizin kapsama hedeflerine ulaşırken maliyetleri düşürmeniz gerekir (çizgi, dal, yol, blok, yöntem, sınıf, denklik sınıfı, kullanım durumu ... Her neyse).
Bu nedenle kapsamı sağlamak için araçlar kullanın ve testlerinizi en düşük maliyete (kısa ve uzun vadeli) neden olacak şekilde tasarlayın. Bazen tüm uygulamanın sadece ana giriş noktasını test ederek ihtiyacınız olan tüm kapsamı kolayca alabilirsiniz. Daha sık olarak, çeşitli iç giriş noktalarında testler yaparak kapsam kazanma maliyetlerini azaltabilirsiniz. Bu kavram yöntem görünürlüğünde durmuyor.
Genellikle iyi modül tasarımı, kamu tarafından ilan edilen giriş noktalarına karşı test yapılmasını en ucuz hale getirir, ancak bu garanti edilmez.
Özel vs kamu önemli değil.
Tüm özel yöntemleri doğrudan test ederek testleri daha pahalı hale getirmeyin ve bazı yöntemlere doğrudan çağrı yapmaktan kaçınarak testleri daha pahalı hale getirmeyin.
Hayır Özel Yöntemleri test etmemelisiniz neden? ve dahası, Mockito gibi popüler alaycı çerçeve, özel yöntemlerin test edilmesini desteklememektedir.
Birim Test kavramını asla anlayamıyorum ama şimdi amacın ne olduğunu biliyorum.
A Birim Testi tam bir test değil. Yani, KG ve manuel test için bir yedek değil. TDD kavramı bu yönden yanlıştır, çünkü özel yöntemler de dahil olmak üzere her şeyi test edemezsiniz, aynı zamanda kaynakları kullanan yöntemler (özellikle kontrolümüz olmayan kaynaklar). TDD, tüm kalitesine ulaşılamayacak bir şey olduğu üzerine kurulu.
Bir Birim testi daha fazla pivot testidir Bazı rasgele pivotları işaretlersiniz ve pivotun sonucu aynı kalmalıdır.