Üniteyi test etmek için özel bir yöntemi herkese açık hale getirmek… iyi fikir mi?


301

Moderatör Not: Burada zaten 39 cevap gönderildi (bazıları silindi). Eğer göndermeden önce senin cevabını, tartışmanın anlamlı bir şey eklemek mümkün olup olmadığını düşünün. Muhtemelen başka birinin söylediklerini tekrar ediyorsunuz.


Bazen kendimi sadece bazı birim testleri yazmak için bir sınıfta özel bir yöntem yapmaya ihtiyaç duyuyorum.

Genellikle bu yöntem sınıftaki diğer yöntemler arasında paylaşılan mantık içerdiğinden ve mantığı kendi kendine test etmek için daha düzenli olduğundan veya senkronize dişlerde kullanılan mantığı diş açma problemleri hakkında endişelenmeden test etmek isteyebileceğimden dolayı olabilir. .

Diğer insanlar bunu yaparken buluyorlar mı, çünkü gerçekten yapmaktan hoşlanmıyorum ?? Şahsen ikramiyelerin, sınıf dışında gerçekten herhangi bir hizmet sağlamayan bir yöntemi halka açık hale getirme sorunlarından daha ağır bastığını düşünüyorum ...

GÜNCELLEME

Herkese cevaplar için teşekkürler, insanların ilgisini çekmiş gibi görünüyor. Bence genel fikir birliği, bir sınıfın kullanılabileceği tek yol olduğu için testin genel API aracılığıyla yapılması gerektiğini düşünüyorum ve bunu kabul ediyorum. Yukarıda bunu yapacağım yukarıda bahsettiğim birkaç vaka nadirdi ve bunu yapmanın yararlarının buna değdiğini düşündüm.

Ancak herkesin bunun gerçekten olmaması gerektiğini gösterdiğini görebiliyorum. Ve biraz daha düşündüğümde, testlere uyum sağlamak için kodunuzu değiştirmenin kötü bir fikir olduğunu düşünüyorum - sonuçta testin bir şekilde bir destek aracı olduğunu ve eğer isterseniz bir destek aracını destekleyecek bir sistemi değiştirmenin açık olduğunu düşünüyorum. kötü uygulama.


2
"bir sistemi 'bir destek aracını destekleyecek' şekilde değiştirmek çok kötü bir uygulamadır." Evet, bir çeşit. Ama OTOH sistem kurulmuş araçları ile de çalışmıyorsa, belki bir şey olduğunu sistemiyle yanlış.
Thilo


1
Bir cevap değil, ama onlara her zaman düşünerek erişebilirsiniz.
Zano

33
Hayır, onları ifşa etmemelisin. Bunun yerine, sınıfınızın genel API'sı aracılığıyla olması gerektiği gibi davrandığını test etmelisiniz. Ve eğer içselleri açığa çıkarmak gerçekten tek seçenekse (şüpheliyim), en azından erişimciler paketini korumalı yapmalısınız, böylece sadece aynı paketteki sınıflar (testiniz olması gerektiği gibi) bunlara erişebilir.
JB Nizet

4
Evet öyle. Birim testlerinden erişilebilir kılmak için bir yöntem paketine özel (varsayılan) görünürlük vermek tamamen kabul edilebilir. Guava gibi kütüphaneler @VisibileForTestingbu tür yöntemleri yapmak için bir açıklama bile sağlar - bunu, yöntemin olmamasının nedeni privatebelgelenecek şekilde yapmanızı öneririm .
Örümcek Boris

Yanıtlar:


186

Not:
Bu yanıt aslen soru testi için gönderilmiştir Ünite testi tek başına özel örnek değişkenleri alıcılar aracılığıyla ortaya çıkarmak için iyi bir neden midir? Bu birleştirilmiş, bu yüzden orada sunulan usecase biraz özel olabilir.

Genel bir açıklama olarak, ben genellikle "üretim" kodu yeniden test için kolaylaştırmak için yeniden. Ancak, bunun iyi bir çağrı olacağını düşünmüyorum. İyi bir birim testi (genellikle), sınıfın uygulama ayrıntılarını değil, yalnızca görünür davranışını dikkate almalıdır. Bunun yerine testine iç yığınlar ortaya çıkarmak gibi, sınıf aradığınız sonra bunu bekliyoruz sırayla sayfaları döndüren test edebilir first()ya last().

Örneğin, şu sahte kodu düşünün:

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

8
Cevabınıza tamamen katılıyorum. Birçok geliştirici, varsayılan erişim değiştiricisini kullanmaya ve iyi açık kaynak çerçevelerinin bu stratejiyi kullandığını doğrulamak için cazip olacaktır. Sadece yapabileceğin için yapman gerektiği anlamına gelmez. +1
CKing

6
Diğerleri yararlı bilgiler sağlamasına rağmen, bu soruna en uygun bulduğum çözüm olduğunu söylemeliyim. Testleri, amaçlanan davranışın dahili olarak nasıl elde edildiğine tamamen agnostik tutar. 10!
Gitahi Ng'ang'a

Bu arada, @Mureinik tarafından sağlanan örnekte, test yöntemlerinin test edilen gerçek yöntemden daha fazlasını kapsaması doğru mu? Örneğin, testFirst () işlevi, burada gerçekten test edilen yöntem olmayan next () öğesini çağırır, önce () kullanılır. Tüm 4 navigasyon yöntemini kapsayan 1 test yöntemine sahip olmak daha temiz ve net olmaz mıydı?
Gitahi Ng'ang'a

1
Bu ideal olsa da, bir bileşenin sadece başarılı olduğunu değil, doğru bir şekilde doğru bir şey yaptığını kontrol etmeniz gereken zamanlar vardır. Bu kara kutu testi için daha zor olabilir. Bazen beyaz kutuyu bir bileşeni test etmeniz gerekir.
Peter Lawrey

1
Sadece birim test uğruna alıcılar mı oluşturmak istiyorsunuz? Bu kesinlikle arayüzün veriyi değil davranışı ortaya koyması gerektiği OOP / Nesne Düşüncesi'ne aykırıdır .
IntelliData

151

Şahsen, kamu API kullanarak yerine birim testi olur ve kesinlikle özel yöntem herkese açık hale asla sadece testin kolay yapmak.

Özel yöntemi gerçekten ayrı olarak test etmek istiyorsanız, Java'da bunu yapmanıza izin vermek için Easymock / Powermock kullanabilirsiniz .

Bu konuda pragmatik olmalısınız ve ayrıca işlerin test edilmesinin zor olmasının nedenlerinin de farkında olmalısınız.

' Testleri dinleyin ' - eğer test etmek zorsa, bu size tasarımınız hakkında bir şey mi söylüyor? Bu yöntem için yapılan bir testin kamuya açık API üzerinden test edilerek önemsiz ve kolay bir şekilde kapsanabileceği durumu yeniden değerlendirebilir misiniz?

İşte, Michael Tüyler 'söylemek zorunda olduğunu Etkili Legacy koduyla çalışma "

"Birçok kişi bu sorunun üstesinden nasıl geleceğini bulmak için çok zaman harcıyor ... asıl cevap, özel bir yöntemi test etme isteğiniz varsa, yöntemin özel olmaması gerekir; eğer yöntemi herkese açık hale getirirseniz seni rahatsız ediyor, şansı, bunun nedeni ayrı bir sorumluluğun parçası olması; başka bir sınıfta olması. " [ Eski Yasalarla Etkili Çalışma (2005) M. Feathers]


5
Easymock / Powermock için +1 ve asla özel bir yöntemi herkese açık hale getirme
Leonard Brünings

51
+1 "Testleri dinleyin" için. Özel bir yöntemi test etme ihtiyacı hissediyorsanız, bu yöntemdeki mantık o kadar karmaşıktır ki ayrı bir yardımcı sınıfta olmalıdır. Sonra yardımcı sınıfı test edebilirsiniz.
Phil

2
'Testleri dinle' kapalı görünüyor. İşte önbellek bağlantısı: webcache.googleusercontent.com/…
Jess Telford

1
@Phil (özellikle kitap referansı) ile hemfikirim, ancak kendimi genellikle eski kodlu bir tavuk / yumurta durumunda buluyorum. Bu yöntemin başka bir sınıfa ait olduğunu biliyorum, ancak ilk etapta testler olmadan% 100 rahat refactoring değilim.
Kevin

Doktoru kabul ediyorum. Özel bir yöntemi test etmeniz gerekiyorsa, muhtemelen başka bir sınıfta olmalıdır. Özel yöntemin diğer sınıf / yardımcı sınıfının genel / korunma olasılığı yüksek olduğunu not edebilirsiniz.
rupweb

67

Diğerlerinin söylediği gibi, özel yöntemleri hiç bir şekilde birim test etmek şüphelidir; birim özel uygulama ayrıntılarını değil, genel arayüzü test eder.

Bununla birlikte, C # 'da özel bir şeyi test etmek istediğimde kullandığım teknik, erişilebilirlik korumasını özelden iç kısma düşürmek ve daha sonra InternalsVisibleTo kullanarak bir birim montajı olarak işaretlemektir . Daha sonra birim test düzeneğinin iç kısımları halka açık olarak ele almasına izin verilir, ancak yanlışlıkla kamu yüzey alanınıza ekleme konusunda endişelenmenize gerek yoktur.


Ben de bu tekniği kullanmak, ama genellikle eski kod için son çare olarak. Özel kodu test etmeniz gerekiyorsa, eksik bir sınıfınız var / test altındaki sınıf çok fazla yapıyor. Söz konusu kodu, ayrı ayrı test edebileceğiniz yeni bir sınıfa çıkarın. Bunun gibi numaralar nadiren kullanılmalıdır.
Finglas

Ancak bir sınıfın ayıklanması ve yalnızca test edilmesi, testlerinizin orijinal sınıfın gerçekten ayıklanan kodu kullandığını bildiği anlamına gelir . Testlerinizde bu boşluğu doldurmanızı nasıl önerirsiniz? Ve eğer cevap orijinal sınıfın genel arayüzünü çıkarılan mantığın kullanılmasına neden olacak şekilde test etmekse, neden ilk etapta onu çıkarmaya zahmet ediyorsunuz? (Burada çatışmacı gelmenin anlamı yok, btw - İnsanların böyle değiş tokuşları nasıl dengelediğini hep merak ettim.)
Mal Ross

@mal Bir işbirliği testim olurdu. Başka bir deyişle, yeni sınıfın doğru bir şekilde çağrıldığını doğrulayan bir alay. Cevabımı aşağıda görebilirsiniz.
Finglas

Test etmek istediğiniz dahili mantıkla sınıftan miras alan bir test sınıfı yazabilir ve dahili mantığı test etmek için kullanılacak genel bir yöntem sağlayabilir misiniz?
sholsinger

1
@sholsinger: C # 'ta ortak sınıf dahili bir sınıftan devralmayabilir.
Eric Lippert

45

Birçok yanıt yalnızca ortak arabirimi test etmeyi önerir, ancak IMHO bu gerçekçi değildir - bir yöntem 5 adım süren bir şey yaparsa, bu beş adımı ayrı ayrı test etmek isteyeceksiniz, hep birlikte değil. Bu, aksi takdirde (test dışında) olabilecek beş yöntemin tümünü de test etmeyi gerektirir private.

"Özel" yöntemleri test etmenin olağan yolu, her sınıfa kendi arayüzünü vermek ve "özel" yöntemleri yapmak public, ancak bunları arayüze dahil etmektir. Bu şekilde, hala test edilebilirler, ancak arayüzü şişirmezler.

Evet, bu dosya ve sınıf bloatlarına neden olur.

Evet, bu publicve privatebelirteçleri gereksiz kılar .

Evet, bu kıçta bir acı.

Ne yazık ki, kodu test edilebilir yapmak için yaptığımız birçok fedakarlıktan biri budur . Belki de gelecekteki bir dil (veya C # / Java'nın gelecekteki bir sürümü) sınıf ve modül test edilebilirliğini daha kolay hale getirecek özelliklere sahip olacaktır; ama bu arada, bu çemberlerden atlamak zorundayız.


Bu adımların her birinin kendi sınıfı olması gerektiğini iddia eden bazıları var , ama katılmıyorum - eğer hepsi devlet paylaşıyorsa, beş yöntemin yapacağı beş ayrı sınıf oluşturmak için bir neden yoktur. Daha da kötüsü, bu dosya ve sınıf-bloat ile sonuçlanır. Ayrıca , modülünüzün genel API'sına da bulaşır - publicbunları başka bir modülden test etmek istiyorsanız mutlaka tüm bu sınıflar olmalıdır (ya da test kodunu aynı modüle dahil edin, yani test kodunu ürününüzle birlikte gönderin) .


2
1. yarı için güzel cevap
cmcginty

7
+1: Benim için en iyi cevap. Bir otomobil üreticisi arabanın bir kısmını aynı nedenden dolayı test etmezse, kimsenin onu satın alacağından emin değilim. Kodun önemli bir kısmı, herkese açık olarak değiştirsek bile test edilmelidir.
Tae-Sung Shin

1
Çok güzel bir cevap. Benim sıcak oyum var. Bir cevap ekledim. İlginizi çekebilir.
davidxxx

29

Bir birim testi, bir sınıfın kodun diğer bölümlerinde nasıl kullanılabileceğini tek şekilde kamu sözleşmesini test etmelidir. Özel bir yöntem uygulama ayrıntılarıdır, herkese açık API doğru çalıştığı sürece uygulama önemli değildir ve test senaryolarında değişiklik yapılmadan değiştirilebilir.



Afair, Bir test durumunda ayrıcalık kullanmaya karar verdiğim nadir durum, bir nesne doğruluğunu doğruladığım zamandı - tüm JNI işleyicilerini kapatmışsa. Açıkçası, genel API olarak gösterilmez, ancak gerçekleşmezse bir bellek sızıntısı meydana gelir. Ama komik, daha sonra genel API'nın bir parçası olarak bir bellek sızıntısı dedektörünü tanıttıktan sonra da kurtuldum.
kan

11
Bu kusurlu bir mantık. Birim testinin amacı hataları daha sonra yakalamaktır. Özel yöntemleri test etmeyerek, testinizde daha sonra bulunamayacak boşluklar bırakıyorsunuz. Özel yöntemin karmaşık olduğu ve halka açık bir arayüzle kolayca uygulanamadığı durumlarda, birim test edilmelidir.
cmcginty

6
@Casey: Eğer özel yöntem halka açık bir arayüz tarafından kullanılmıyorsa neden orada?
Thilo

2
@DaSilva_Ireland: Gelecekte test senaryolarını sürdürmek zor olacak. Tek gerçek sınıf kullanım durumu genel yöntemdir. Yani diğer tüm kodlar sınıfı yalnızca ortak API ile kullanabilir. Bu nedenle, uygulama ve özel yöntem test durumunun başarısız olmasını değiştirmek istiyorsanız, yanlış bir şey yaptığınız anlamına gelmez, ayrıca, yalnızca özel test ediyorsanız, ancak genel olarak tam olarak test etmiyorsanız, bazı potansiyel hataları kapsamaz . İdeal olarak bir test senaryosu tüm olası vakaları ve sadece sınıfın gerçek kullanım vakalarını kapsamalıdır.
kan

22

IMO, testlerinizi, sınıfınızın içeride nasıl uygulandığına dair derin varsayımlar yapmadan yazmalısınız. Muhtemelen daha sonra başka bir dahili model kullanarak yeniden düzenleme yapmak istiyorsunuz, ancak yine de önceki uygulamanın verdiği garantileri aynen yapıyorsunuz.

Bunu akılda tutarak, sınıfınızda şu anda hangi dahili uygulamaya sahip olursanız olun sözleşmenizin hala geçerli olduğunu test etmeye odaklanmanızı öneririm. Herkese açık API'larınızın mülke dayalı testi.


6
Kodun yeniden düzenlenmesi sonrasında yeniden yazılması gerekmeyen birim testlerin daha iyi olduğunu kabul ediyorum. Ve sadece ünite testlerini yeniden yazmak için harcadığınız zamandan dolayı değil. Çok daha önemli bir neden, birim testlerin en önemli amacının, kodunuzun bir yeniden düzenleme işleminden sonra hala doğru şekilde davranması olduğunu düşünmem. Bir yeniden düzenleme işleminden sonra tüm birim testlerinizi yeniden yazmanız gerekiyorsa, birim testlerinin bu avantajını kaybedersiniz.
kasperd

20

Paketi özel yapmaya ne dersiniz? Daha sonra test kodunuz onu (ve paketinizdeki diğer sınıfları da) görebilir, ancak yine de kullanıcılarınızdan gizlidir.

Ama gerçekten, özel yöntemleri test etmemelisiniz. Bunlar uygulama detaylarıdır ve sözleşmenin bir parçası değildir. Yaptıkları her şey kamu yöntemlerini çağırarak ele alınmalıdır (orada kamu yöntemlerinin kullanmadığı bir kod varsa, o zaman gitmelidir). Özel kod çok karmaşıksa, sınıf muhtemelen çok fazla şey yapıyor ve yeniden düzenleme yapmak istiyor.

Bir yöntemi halka duyurmak büyük bir taahhüttür. Bunu yaptıktan sonra, insanlar bunu kullanabilir ve artık onları değiştiremezsiniz.


Özelliğinizi test etmeniz gerekiyorsa, muhtemelen bir sınıf eksik
jk.

Eksik sınıf için +1. Başkalarının hemen "işaretlemek" bandwagon atlamak görmek sevindim.
Finglas

Çocuk paketin özel cevabını bulmak için uzun bir yol kat etmem gerekti.
Bill K

17

Güncelleme : Bu soruya başka birçok yerde daha geniş ve eksiksiz bir cevap ekledim. Bu blogumda bulunabilir .

Test etmek için bir şeyi herkese açık hale getirmem gerekirse, bu genellikle test edilen sistemin Tek Tekrarlanabilirlik İlkesine uymadığını ima eder . Dolayısıyla, tanıtılması gereken eksik bir sınıf vardır. Kodu yeni bir sınıfa çıkardıktan sonra herkese açık hale getirin. Şimdi kolayca test edebilirsiniz ve SRP'yi takip ediyorsunuz. Diğer sınıfınız sadece bu yeni sınıfı kompozisyonla çağırmak zorundadır.

Metodları herkese açık hale getirme / test montajları için görünür kod işaretleme gibi dil hilelerini kullanma her zaman son çare olmalıdır.

Örneğin:

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

Doğrulayıcı bir nesne ekleyerek bunu yeniden düzenleyin.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

Şimdi tek yapmamız gereken doğrulayıcının doğru şekilde çağrıldığını test etmektir. Gerçek doğrulama süreci (önceden özel mantık) saf izolasyonda test edilebilir. Bu doğrulamanın başarılı olmasını sağlamak için karmaşık test kurulumuna gerek kalmayacaktır.


1
Şahsen SRP'nin çok ileri götürülebileceğini düşünüyorum, örneğin bir uygulamada hesapları güncelleme / oluşturma / silme ile ilgili bir hesap hizmeti yazarsam, bana her işlem için ayrı bir sınıf oluşturmanız gerektiğini mi söylüyorsunuz? Örneğin, validasyon hakkında başka bir yerde asla kullanılmayacaksa, validasyon mantığını başka bir sınıfa çıkarmaya değer mi? imo sadece kodu daha az okunabilir, daha az özlü yapar ve uygulamanızı gereksiz sınıflarla paketler!
jcvandan

1
Her şey senaryoya bağlı. Bir kişinin "bir şeyi yapması" tanımı, anneler için farklı olabilir. Bu durumda kendiniz için açıkça test etmek istediğiniz özel kod karmaşıktır / kurulması bir acıdır. Test etmek istediğiniz gerçek çok fazla şey yaptığını kanıtlıyor. Bu nedenle evet, yeni bir nesneye sahip olmak ideal olacaktır.
Finglas

1
Kodunuzu daha az okunabilir hale getirmeye gelince, kesinlikle katılmıyorum. Doğrulama için bir sınıfa sahip olmak, doğrulamanın diğer nesneye gömülmesinden daha kolay okunur. Hala aynı miktarda kod var, sadece büyük bir sınıfta iç içe değil. Bir şeyi çok iyi yapan birkaç sınıfı tercih ederim, tek bir büyük sınıftan.
Finglas

4
@dormisher, hesap hizmetinizle ilgili olarak, SRP'yi "değiştirmek için tek bir sebep" olarak düşünün. CRUD operasyonlarınız doğal olarak birlikte değişecekse, SRP'ye uyacaktır. Normalde bunları değiştirirsiniz çünkü veritabanı şeması değişebilir;
Anthony Pegram

@finglas bu konuda ne düşünüyorsun: stackoverflow.com/questions/29354729/… . Özel yöntemi test etmek istemiyorum, bu yüzden belki de bir sınıfa ayırmamalıyım, ancak diğer 2 genel yöntemde kullanılıyor, bu yüzden aynı mantığın iki katını test ediyorum.
Rodrigo Ruiz

13

Bazı harika cevaplar. Bahsetmediğim bir şey, test odaklı geliştirme (TDD) ile, yeniden düzenleme aşamasında özel yöntemlerin yaratıldığı ( yeniden düzenleme modelinin bir örneği için Ekstraksiyon Yöntemine bakın) ve bu nedenle zaten gerekli test kapsamına sahip olması gerektiğidir. . Doğru bir şekilde yapılırsa (ve elbette, doğruluk söz konusu olduğunda karışık bir fikir çantası alacaksınız), test edebilmeniz için özel bir yöntemi herkese açık hale getirme konusunda endişelenmenize gerek yoktur.


11

Neden yığın yönetimi algoritmasını bir yardımcı program sınıfına ayırmıyorsunuz? Hizmet sınıfı yığınları yönetebilir ve genel erişim sağlayabilir. Birim testleri uygulama detaylarına odaklanabilir. Algoritmik olarak zor sınıflar için derin testler, uç vakaların kırışmasında ve kapsamın sağlanmasında çok yardımcıdır.

Ardından, geçerli sınıfınız, uygulama ayrıntılarını göstermeden yardımcı program sınıfına temiz bir şekilde yetki verebilir. Testleri, diğerlerinin önerdiği gibi sayfalandırma gereksinimi ile ilgilidir.


10

Java'da, paketi özel yapma seçeneği de vardır (örn. Görünürlük değiştiricisini bırakma). Birim testleriniz test edilen sınıfla aynı pakette ise, bu yöntemleri görebilmeli ve yöntemi tamamen herkese açık hale getirmekten biraz daha güvenli olmalıdır.


10

Özel yöntemler genellikle "yardımcı" yöntemler olarak kullanılır. Bu nedenle, yalnızca temel değerleri döndürürler ve asla nesnelerin belirli örneklerinde çalışmazlar.

Test etmek istiyorsanız birkaç seçeneğiniz vardır.

  • Yansımayı kullan
  • Yöntem paketine erişim izni verin

Alternatif olarak, yeni bir sınıf için yeterince iyi bir aday ise, genel yöntem olarak yardımcı yöntemle yeni bir sınıf oluşturabilirsiniz.

Burada bu konuda çok iyi bir makale var .


1
Adil yorum. Sadece bir seçenek, belki de kötü bir seçimdi.
adamjmarkham

@Finglas Özel kodu yansıma kullanarak test etmek neden kötü bir fikirdir?
Anshul

@Anshul özel yöntemleri, davranış değil uygulama ayrıntılarıdır. Başka bir deyişle, değişirler. İsimler değişir. Parametreler değişir. İçerik değişiyor. Bu, bu testlerin her zaman kırılacağı anlamına gelir. Öte yandan kamuya açık yöntemler API açısından çok daha kararlıdır, bu nedenle daha esnektir. Genel yöntemlerle ilgili testlerinizin uygulama ayrıntılarını değil davranışı kontrol etmesini sağlayarak iyi olmalısınız.
Finglas

1
@Finglas Uygulama ayrıntılarını neden test etmek istemiyorsunuz? Çalışması gereken koddur ve daha sık değişirse, daha sık kırılmasını beklersiniz, bu da genel API'dan daha fazla test etmek için daha fazla nedendir. Kod tabanınız kararlı hale geldikten sonra, diyelim ki gelecekte birileri gelir ve sınıfınızın dahili olarak nasıl çalıştığını bozan özel bir yöntemi değiştirir. Sınıfınızın içindekileri test eden bir test, derhal onlara bir şey kırdıklarını söylerdi. Evet, testlerinizi sürdürmeyi zorlaştıracaktır, ancak bu tam kapsamlı testlerden beklediğiniz bir şeydir.
Anshul

1
@Finglas Uygulama ayrıntılarını test etmek yanlış değil. Bu sizin tavrınız, ama bu çok tartışılan bir konudur. İç kısımları test etmek, tam kapsama alanı için gerekli olmasalar bile, size birkaç avantaj sağlar. Testleriniz daha odaklıdır, çünkü testinizi sınırlandırabilecek genel API'yı kullanmak yerine özel yöntemleri doğrudan test ediyorsunuz. Özel üyeleri manuel olarak geçersiz kılabileceğiniz ve genel API'nın tutarsız dahili duruma yanıt olarak uygun hataları döndürdüğünden emin olabileceğiniz için negatif test daha kolaydır. Daha fazla örnek var.
Anshul

9

C # kullanıyorsanız yöntemi dahili yapabilirsiniz. Bu şekilde herkese açık API'yı kirletmezsiniz.

Sonra dll'ye öznitelik ekleyin

[assembly: InternalsVisibleTo ("MyTestAssembly")]

Artık tüm yöntemler MyTestAssembly projenizde görülebilir. Belki mükemmel değil, ancak özel yöntemi sadece test etmek için herkese açık hale getirmek daha iyidir.


7

Gerekirse özel değişkenlere erişmek için yansımayı kullanın.

Ama gerçekten, sınıfın iç durumunu umursamıyorsunuz, sadece genel yöntemlerin tahmin edebileceğiniz durumlarda beklediğinizi geri getirdiğini test etmek istiyorsunuz.


Geçersiz bir yöntemle ne yapardınız?
IntelliData

@IntelliData Gerekirse özel değişkenlere erişmek için yansımayı kullanın.
Daniel Alexiuc

6

Kötü bir fikir olduğunu söyleyebilirim, çünkü herhangi bir fayda ve potansiyel olarak sorun yaşamayacağınızdan emin değilim. Çağrıların sözleşmesini değiştiriyorsanız, sadece özel bir yöntemi test etmek için, sınıfı nasıl kullanılacağı konusunda test etmiyorsunuz, ancak asla gerçekleşmesini istemediğiniz yapay bir senaryo oluşturuyorsunuz.

Ayrıca, yöntemi herkese açık olarak ilan ederek, altı ay içinde (bir yöntemi herkese açık hale getirmenin tek sebebinin test etmek olduğunu unutduktan sonra) sizin (veya projeyi teslim ettiyseniz) tamamen farklı birinin potansiyel olarak istenmeyen sonuçlara ve / veya bakım kabusuna yol açmaz.


1
sınıf bir hizmet sözleşmesinin uygulanması ve yalnızca arayüzü üzerinden kullanılıyorsa - bu şekilde özel yöntem herkese açık hale
getirilse

Yine de sınıfı genel API'yi (arabirimi) kullanarak test etmek isterim, aksi takdirde dahili yöntemi bölmek için sınıfı yeniden düzenlerseniz, testleri değiştirmeniz gerekir, bu da bazı harcamalar yapmanız gerektiği anlamına gelir. yeni testlerin eski testlerle aynı şekilde çalışmasını sağlama çabası.
beny23

Ayrıca, test kapsamının özel bir yöntemin içindeki her uç durumu içermesini sağlamanın tek yolu, doğrudan çağırmaksa, sınıf karmaşıklığının, basitleştirmek için yeniden düzenlemeyi gerektirdiğinin göstergesi olabilir.
beny23

@dormisher - Sınıf yalnızca arabirim üzerinden kullanılıyorsa, yalnızca genel arabirim yöntemlerinin doğru davrandığını sınamanız gerekir. Herkese açık yöntemler yapmak istediklerini yaparsa neden özel yöntemlerle ilgileniyorsunuz?
Andrzej Doyle

Sanırım gerçekten yapmamalıyım, ancak geçerli olabileceğine inandığım durumlar var, örneğin model durumunu doğrulayan özel bir yöntem, böyle bir yöntemi test etmeye değer olabilir - ancak böyle bir yöntem test edilebilir olmalı yine de ortak arayüz
jcvandan

6

birim testi açısından, kesinlikle daha fazla yöntem eklememelisiniz; Her bir testten first()önce çağrılacak yönteminizle ilgili bir test durumu daha iyi yapacağınıza inanıyorum ; o zaman birden çok kez çağırabilir - next(), previous()ve last()sonuçları beklentilerinizi maç olmadığını görmek için. Sınıfınıza daha fazla yöntem eklemezseniz (sadece test amacıyla), "kara kutu" test prensibine bağlı kalacağınızı tahmin ediyorum;


5

Öncelikle yöntemin başka bir sınıfa çıkarılıp kamuya duyurulması gerekip gerekmediğine bakın. Durum böyle değilse, paketi korumalı hale getirin ve Java'da @VisibleForTesting ile açıklama ekleyin .


5

Güncellemenizde, herkese açık API'yı kullanarak test yapmanın iyi olduğunu söylüyorsunuz. Aslında burada iki okul var.

  1. Kara kutu testi

    Kara kutu okulu, sınıfın kimsenin içindeki uygulamayı göremeyeceği bir kara kutu olarak düşünülmesi gerektiğini söylüyor. Bunu test etmenin tek yolu, tıpkı sınıf kullanıcısının kullanacağı gibi, herkese açık API yoluyladır.

  2. beyaz kutu testi.

    Beyaz kutu okulu, doğal olarak, sınıfın uygulanması hakkındaki bilgileri kullanmayı ve daha sonra sınıfın gerektiği gibi çalıştığını bilmek için test etmeyi düşünüyor.

Tartışmaya gerçekten katılamıyorum. Sadece bir sınıfı (ya da bir kütüphaneyi ya da her neyse) test etmenin iki farklı yolu olduğunu bilmenin ilginç olacağını düşündüm.


4

Aslında bunu yapmanız gereken durumlar vardır (örneğin bazı karmaşık algoritmalar uygularken). Sadece paket özel yapın ve bu yeterli olacaktır. Ancak çoğu durumda, muhtemelen diğer sınıflara mantığı çarpanlarına ayırmayı gerektiren çok karmaşık sınıflarınız vardır.


4

Tek başına test etmek istediğiniz özel yöntemler, sınıfınızda gömülü başka bir "kavram" olduğunun bir göstergesidir. Bu "konsepti" kendi sınıfına çıkarın ve ayrı bir "birim" olarak test edin.

Konuya gerçekten ilginç bir bakış için bu videoya bir göz atın .


4

Testlerinizin kodunuzu dikte etmesine asla izin vermemelisiniz. TDD veya diğer DD'lerden bahsetmiyorum, tam olarak ne istediğini. Uygulamanızın herkese açık olması için bu yöntemlere ihtiyacı var mı? Eğer öyleyse onları test edin. Eğer yapmazsa, sadece test için onları halka açmayın. Değişkenler ve diğerleri ile aynı. Uygulamanızın ihtiyaçlarının kodu dikte etmesine izin verin ve testlerinizin ihtiyacın karşılandığını test etmesine izin verin. (Yine ilk olarak test etmek demek istemiyorum ya da değil, test amacına ulaşmak için bir sınıf yapısını değiştirmek demek istiyorum).

Bunun yerine "daha yüksek test yapmalısınız". Özel yöntemi çağıran yöntemi test edin. Ancak testleriniz "uygulama kararlarınızı" değil, uygulama ihtiyaçlarınızı test ediyor olmalıdır.

Örneğin (bod sözde kodu burada);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

Bunun yerine "kitaplar" ı test etmek için "ekle" yi test etmek için bir neden yoktur.

Testlerinizin sizin için kod tasarımı kararları vermesine asla izin vermeyin. Bu sonucu nasıl alacağınızı değil, beklenen sonucu elde ettiğinizi test edin.


1
"Testlerinizin sizin için kod tasarımı kararları vermesine asla izin vermeyin" - Katılmıyorum. Test edilmesi zor bir kod kokusudur. Test edemezseniz, kırık olmadığını nasıl anlarsınız?
Alfred Armstrong

Açıklamak için, örneğin bir kütüphanenin harici API tasarımını değiştirmemeniz gerektiğini kabul ediyorum. Ancak daha test edilebilir hale getirmek için yeniden düzenleme yapmanız gerekebilir.
Alfred Armstrong

Test etmek için refactor gerekiyorsa, endişelenmeniz gereken kod kokusu olmadığını buldum. Başka bir şeyiniz var. Tabii ki bu kişisel bir deneyim ve muhtemelen her ikisinde de katı verilerle bir tartışma yapabiliriz. Sadece "test etmek zor" olmadı, ilk etapta "kötü tasarım" değildi.
coteyr

Bu çözüm gibi bir yöntem için güzel kitaplar bir değer döndürür - Bir dönüş türünü kontrol edebilirsiniz assert ; ancak geçersiz bir yöntemi nasıl kontrol edersiniz ?
IntelliData

void yöntemleri bir şey yapar ya da var olmaları gerekmez. Yaptıkları bir şeyi kontrol et,
coteyr

2

Birimi test ettirme bonuslarının, bazı üyelerin görünürlüğünü artırma sorunlarından daha ağır bastığı konusunda hemfikirim. Küçük bir gelişme, korumalı ve sanal hale getirmektir, daha sonra ortaya çıkarmak için bir test sınıfında geçersiz kılınır.

Alternatif olarak, ayrı ayrı test etmek istediğiniz işlevsellik varsa, tasarımınızda eksik bir nesne bulunmuyor mu? Belki onu ayrı bir test edilebilir sınıfa koyabilirsiniz ... o zaman mevcut sınıfınız bu yeni sınıfın bir örneğine yetki verir.


2

Test sınıflarını genellikle test edilen sınıflarla aynı proje / montajda tutarım.
Bu şekilde sadece ihtiyacım varinternal fonksiyonları / sınıfları test edilebilir kılmak için görünürlüğe .

Bu, test sınıflarını filtrelemesi gereken yapım sürecinizi biraz karmaşıklaştırır. Bunu tüm test sınıflarımı isimlendirerek TestedClassTestve bu sınıfları filtrelemek için bir normal ifade kullanarak başarıyorum.

Bu elbette sorunuzun C # / .NET kısmı için geçerlidir


2

Sık sık bir şeyler gibi adlı bir yöntem katacak validate, verify,check bir nesnenin iç durumu test etmek için çağrılabilir, böylece bir sınıfa, vb.

Bazen bu yöntem bir ifdef bloğuna sarılır (çoğunlukla C ++ ile yazılır), böylece serbest bırakılması için derlenmez. Ancak, çoğu zaman, programın nesne ağaçlarını bir şeyleri kontrol ederek yürütebilen doğrulama yöntemleri sağlamak için genellikle faydalıdır.


2

Guava, aksi takdirde yapacakları kapsamı (paket veya genel) olan işaretleme yöntemleri için @VisibleForTesting ek açıklamasına sahiptir. Aynı şey için @Gizli ek açıklama kullanıyorum.

Herkese açık API'nın test edilmesi gerekirken, normalde herkese açık olmayacak şeylere ulaşmak bazen uygun ve mantıklıdır.

Ne zaman:

  • bir sınıf, birden fazla sınıfa ayrılarak, daha az okunabilir hale getirilir,
  • sadece daha test edilebilir hale getirmek için,
  • ve innards için biraz test erişimi sağlamak hile yapardı

din mühendisliği yıkıyor gibi görünüyor.


2

Genellikle bu yöntemleri bırakıyorum protectedve sınıf yükleyicisi onları aynı ad alanına yerleştireceğinden, korunan tüm yöntemlere erişebilecekleri aynı paketin (ancak başka bir proje veya kaynak klasöründe) birim testini yerleştiriyorum.


2

Hayır, çünkü o kediyi daha iyi ciltlemenin yolları var.

Bazı birim test kayışları, sınıf tanımında, test modundayken kanca oluşturmak için otomatik olarak genişleyen makrolara dayanır. Çok C tarzı, ama işe yarıyor.

Daha kolay bir OO deyimi "özel" yerine "korumalı" test etmek istediğiniz her şeyi yapmaktır. Test koşum takımı test edilen sınıftan miras alır ve daha sonra tüm korunan üyelere erişebilir.

Ya da "arkadaş" seçeneğini tercih edersiniz. Şahsen bu en az C ++ kapsülleme kurallarını ihlal çünkü özelliği, ama C ++ bazı özellikleri nasıl uyguladığı için gerekli olur, bu yüzden hey ho.

Her neyse, birim testi yapıyorsanız, o üyelere değer enjekte etme olasılığınız yüksektir. Beyaz kutu manifatura mükemmel bir şekilde geçerlidir. Ve bu gerçekten olur senin kapsülleme bölünürler.


2

Net'te özel bir sınıf var. PrivateObjectNet'te, bir sınıfın özel yöntemlerine erişmenizi sağlamak için özellikle tenezzül özel bir sınıf vardır.

Bununla ilgili daha fazla bilgiyi MSDN'de veya burada Stack Overflow'da görebilirsiniz

(Şimdiye kadar kimsenin bundan bahsetmediğini merak ediyorum.)

Ancak bunun yeterli olmadığı durumlar vardır, bu durumda yansıma kullanmak zorunda kalacaksınız.

Yine de özel yöntemleri test etmemek için genel öneriye uyuyorum, ancak her zamanki gibi her zaman istisnalar var.


1

Diğerlerinin yorumları tarafından kapsamlı bir şekilde belirtildiği gibi, birim testleri genel API'ya odaklanmalıdır. Ancak, artıları / eksileri ve gerekçesi bir yana, yansıma kullanarak bir birim testte özel yöntemleri çağırabilirsiniz. Elbette JRE güvenliğinizin izin verdiğinden emin olmanız gerekir. Özel yöntemler çağırmak, Spring Framework'ün ReflectionUtils ile kullandığı bir şeydir ( makeAccessible(Method)yönteme bakın ).

Özel örnek yöntemine sahip küçük bir örnek sınıf.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

Özel örnek yöntemini yürüten bir örnek sınıf.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

B yürütülmesi, yazdırılacak Doing something private. Gerçekten ihtiyacınız varsa, yansıma özel örnek yöntemlerine erişmek için birim testlerinde kullanılabilir.


0

Şahsen, özel yöntemleri test ederken aynı sorunları yaşıyorum ve bunun nedeni bazı test araçlarının sınırlı olması. İhtiyacınıza cevap vermezlerse tasarımınızı değil, aracı değiştirerek tasarımınızın sınırlı araçlarla sürülmesi iyi değildir. Çünkü C # istemeniz iyi test araçları öneremiyorum, ancak Java için iki güçlü araç var: TestNG ve PowerMock ve .NET platformu için ilgili test araçlarını bulabilirsiniz

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.