Özel bir işlevi veya özel yöntemleri, alanları veya iç sınıfları olan bir sınıfı nasıl test edebilirim?


2728

Dahili özel yöntemleri, alanları veya iç içe sınıfları olan bir sınıfı nasıl test edebilirim (xUnit kullanarak)? Veya iç bağlantıya sahip ( staticC / C ++ 'da) veya özel ( anonim ) bir ad alanında bulunan özel bir işlev mi?

Yalnızca bir test çalıştırabilmek için bir yöntem veya işlevin erişim değiştiricisini değiştirmek kötü görünüyor.


257
Özel bir yöntemi test etmenin en iyi yolu doğrudan test etmektir
Surya


383
Katılmıyorum. Uzun veya anlaşılması zor bir (kamusal) yöntemin yeniden düzenlenmesi gerekir. Sadece halka açık olan yerine küçük (özel) yöntemleri test etmek aptalca olurdu.
Michael Piefel

181
Görünürlüğü aptal olduğu için herhangi bir yöntemi test etmemek. Birim testi bile en küçük kod parçası olmalıdır ve yalnızca herkese açık yöntemleri test ederseniz, hatanın nerede oluştuğundan emin olamayacaksınız - bu yöntem veya başka bir şey.
Dainius

126
Uygulamanın değil , sınıf işlevselliğinin sınanması gerekir . Özel yöntemleri test etmek ister misiniz? Onları çağıran genel yöntemleri test edin. Eğer sınıfın sunduğu işlevsellik iyice test edilirse, içselliği doğru ve güvenilir olduğunu kanıtlamıştır; dahili koşulları test etmeniz gerekmez. Testler, test edilen sınıflardan ayrılmayı sürdürmelidir.
Calimar41

Yanıtlar:


1640

Güncelleme:

Daha sonra belki özel bir yöntem veya herhangi ulaşılmaz üyesini sınamanın en iyi yolu yaklaşık 10 yıl, yoluyladır @Jailbreakgelen Manifold çerçeve.

@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;

Bu şekilde, kodunuz güvenli ve okunabilir kalır. Tasarımdan ödün vermez, testler için fazla pozlama yöntemleri ve alanları yoktur.

Biraz eski bir Java uygulamanız varsa ve yöntemlerinizin görünürlüğünü değiştirmenize izin verilmiyorsa, özel yöntemleri test etmenin en iyi yolu yansıma kullanmaktır .

Dahili olarak çağırmak ve yöntemler privateile private staticdeğişkenleri almak / ayarlamak için yardımcıları kullanıyoruz . Aşağıdaki modeller, özel yöntemler ve alanlarla ilgili hemen hemen her şeyi yapmanıza izin verecektir. Tabii ki, yansıma yoluyla değişkenleri değiştiremezsiniz .privateprivate staticprivate static final

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

Ve alanlar için:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

Notlar:
1. yöntemlere TargetClass.getDeclaredMethod(methodName, argClasses)bakmanızı sağlar private. Aynı şey için de geçerlidir getDeclaredField.
2. Bu setAccessible(true)haklar ile oynamak için gereklidir.


350
API'yi bilmiyorsanız yararlı olabilir, ancak özel yöntemleri bu şekilde test etmek zorundaysanız, tasarımınızla ilgili bir şey var. Başka bir afişin söylediği gibi, birim testi sınıfın sözleşmesini test etmelidir: sözleşme çok genişse ve sistemi çok fazla başlatırsa tasarım ele alınmalıdır.
andygavin

21
Çok kullanışlı. Bunu kullanarak, testlerin gizleme sonrasında çalıştırılmasının kötü bir şekilde başarısız olacağını akılda tutmak önemlidir.
Rick Minerich

20
Örnek kod benim için işe yaramadı, ancak bu perileri daha net hale getirdi: java2s.com/Tutorial/Java/0125__Reflection/…
Rob

35
Doğrudan yansımayı kullanmaktan çok daha iyi, Powermock gibi bir kütüphane kullanmak olacaktır .
Michael Piefel

25
Bu nedenle Test Odaklı Tasarım yararlıdır. Davranışı doğrulamak için nelerin açıklanması gerektiğini anlamanıza yardımcı olur.
Thorbjørn Ravn Andersen

621

Özel bir yöntemi test etmenin en iyi yolu başka bir genel yöntemdir. Bu yapılamazsa, aşağıdaki koşullardan biri doğrudur:

  1. Özel yöntem ölü koddur
  2. Sınıfta test ettiğiniz bir tasarım kokusu var
  3. Test etmeye çalıştığınız yöntem özel olmamalıdır

6
Ayrıca, hiçbir zaman% 100 kod kapsamına asla yaklaşmıyoruz, bu yüzden neden zamanınızı müşterilerin doğrudan kullanacağı yöntemlere kalite testi yaparak odaklamıyorsunuz.
grinch

30
@grinch Spot açık. Özel yöntemleri test ederek elde edebileceğiniz tek şey, hata ayıklama bilgisidir ve hata ayıklayıcılar bunun içindir. Sınıf sözleşmesi testlerinizin kapsamı tamsa, ihtiyacınız olan tüm bilgilere sahip olursunuz. Özel yöntemler bir uygulama detayıdır. Bunları test ederseniz, sözleşmeniz değişmese bile uygulamanız her değiştiğinde testlerinizi değiştirmeniz gerekecektir. Yazılımın tüm yaşam döngüsünde bunun sağladığı faydadan çok daha pahalıya mal olması muhtemeldir.
Erik Madsen

9
@AlexWien haklısın. Kapsama doğru terim değildi. Söylemeliydim, "sınıf sözleşmesi testleriniz tüm anlamlı girdileri ve tüm anlamlı durumları kapsıyorsa" idi. Bu, haklı olarak, genel arabirim aracılığıyla koda veya test ettiğiniz gibi dolaylı olarak test edilemediğini kanıtlayabilirsiniz. Eğer test ettiğiniz bazı kodlar için durum buysa, ben de aşağıdakileri düşünmeye meyilli olurum: sınıf durumuna göre büyük farklılıklar gösterir). Her iki tasarım kokusunu da düşünürdüm.
Erik Madsen

16
Hemen hemen tüm özel yöntemler doğrudan test edilmemelidir (bakım maliyetlerini azaltmak için). İstisna: Bilimsel yöntemlerin f (a (b (c (x), d (y))), a (e (x, z)), b (f (x, y, z), z)) gibi formları olabilir. burada a, b, c, d, e ve f çok karmaşık ifadelerdir, ancak bu tek formülün dışında işe yaramazlar. Bazı işlevleri (MD5'i düşünmek) tersine çevirmek zordur, bu nedenle davranış alanını tamamen kapsayacak parametreleri seçmek zor olabilir (NP-Sert). A, b, c, d, e, f'yi bağımsız olarak test etmek daha kolaydır. Onları yeniden kullanılabilir oldukları herkese açık veya paket özel yalanlar yapmak. Çözüm: Özelleştirin, ancak test edin.
İsimsiz

4
@Eponymous Bu konuda biraz paramparça oldum. İçimdeki nesne yönelimli safçı, statik özel yöntemler yerine nesne yönelimli bir yaklaşım kullanmanız gerektiğini söyleyerek genel örnek yöntemleri aracılığıyla test yapmanızı sağlar. Fakat yine de, bilimsel bir çerçevede, bellek yükünün tamamen statik yaklaşım için savunduğuna inanıyorum.
Erik Madsen

323

Bir sınıfta özel yöntemleri doğrudan test etme ihtiyacını hissettiğim kadar karmaşık olan özel yöntemlere sahip olduğumda, bu bir kod kokusudur: sınıfım çok karmaşık.

Bu tür sorunları ele almak için her zamanki yaklaşımım, ilginç bitleri içeren yeni bir sınıf çıkarmaktır. Genellikle, bu yöntem ve etkileşime girdiği alanlar ve belki başka bir yöntem veya iki yeni bir sınıfa çıkarılabilir.

Yeni sınıf bu yöntemleri 'genel' olarak ortaya koyar, böylece birim testi için erişilebilir olurlar. Yeni ve eski sınıflar artık orijinal sınıftan daha basit, bu benim için harika (işleri basit tutmam gerekiyor ya da kayboluyorum!).

İnsanların beyinlerini kullanmadan sınıflar oluşturmasını önermediğimi unutmayın! Buradaki nokta, iyi yeni sınıflar bulmanıza yardımcı olmak için birim test güçlerini kullanmaktır.


24
Soru, özel yöntemlerin nasıl test edileceği idi. Bunun için yeni bir sınıf yapacağınızı (ve çok daha fazla karmaşıklık ekleyeceğinizi) söylüyorsunuz ve daha sonra yeni sınıf oluşturmamanızı öneriyorsunuz. Peki özel yöntemleri nasıl test edebilirim?
Dainius

27
@Dainius: Yalnızca bu yöntemi test edebilmeniz için yeni bir sınıf oluşturmanızı önermiyorum . Yazma testlerinin tasarımınızı geliştirmenize yardımcı olabileceğini öneririm: iyi tasarımların test edilmesi kolaydır.
Jay Bazuzi

13
Ancak, iyi OOD'nin yalnızca o sınıfın / nesnenin doğru çalışması için gerekli yöntemleri ortaya çıkaracağını (herkese açık hale getireceğini) kabul ediyorsunuz? diğer tüm özel / koruma olmalıdır. Bu nedenle bazı özel yöntemlerde bazı mantık olacaktır ve IMO testi bu yöntemlerin yalnızca yazılım kalitesini artıracaktır. Tabii ki bir kod parçası karmaşık ise, ayrı yöntemlere / sınıfa bölünmesi gerektiğini kabul ediyorum.
Dainius

6
@Danius: Yeni bir sınıf eklemek çoğu durumda karmaşıklığı azaltır. Sadece ölç. Bazı durumlarda test edilebilirlik OOD ile savaşır. Modern yaklaşım iyilik kanıtıdır.
AlexWien

5
Bu tam olarak sürücüye testlerden geribildirim kullanmak ve tasarımını geliştirmek için nasıl! Testlerinizi dinleyin. eğer yazmak zorsa, bu size kodunuz hakkında önemli bir şey söylüyor!
pnschofield

289

Geçmişte Java için bunu yapmak için yansıma kullandım ve bence bu büyük bir hataydı.

Belirtmek gerekir gerektiğini, konuşan değil , doğrudan özel yöntemler test birim testleri yazma. Test etmeniz gereken şey, sınıfın diğer nesnelerle sahip olduğu kamu sözleşmesidir; bir nesnenin içini asla doğrudan test etmemelisiniz. Başka bir geliştirici, sınıfta kamu sözleşmesini etkilemeyen, sınıfta küçük bir iç değişiklik yapmak istiyorsa, çalışmasını sağlamak için yansıma tabanlı testinizi değiştirmeniz gerekir. Bunu bir proje boyunca tekrar tekrar yaparsanız, birim testleri, kod sağlığının yararlı bir ölçümü olmayı bırakır ve gelişmenin önünde bir engel olmaya ve geliştirme ekibine bir sıkıntı olmaya başlar.

Bunun yerine önerdiğim, yazdığınız birim testlerinin özel yöntemlerde kodun iyi bir şekilde kapsanmasını sağlamak için Cobertura gibi bir kod kapsama aracı kullanmaktır. Bu şekilde, özel yöntemlerin ne yaptığını dolaylı olarak test edersiniz ve daha yüksek bir çeviklik sağlarsınız.


76
+ 1'leyin. Bence bu sorunun en iyi cevabı. Özel yöntemleri test ederek uygulamayı test edersiniz. Bu, bir sınıf sözleşmesinin giriş / çıkışlarını test etmek için birim testin amacını yener. Bir test, yalnızca bağımlılıkları için çağırdığı yöntemleri alay edecek uygulama hakkında yeterli bilgiye sahip olmalıdır . Başka bir şey yok. Bir testi değiştirmek zorunda kalmadan uygulamanızı değiştiremezseniz - test stratejinizin zayıf olması ihtimali vardır.
Colin M

10
@Colin M Gerçekten sorduğu şey bu değil;) Bırakmasına karar ver, projeyi bilmiyorsun.
Aaron Marcus

2
Gerçekten doğru değil. Özel yöntemin küçük bir kısmını, onu kullanan genel yöntemle test etmek çok çaba gerektirebilir. Genel yöntem, özel yönteminizi çağıran hatta ulaşmadan önce önemli bir kurulum gerektirebilir
ACV

1
Katılıyorum. Sözleşmenin yerine getirilip getirilmediğini test etmelisiniz. Bunun nasıl yapıldığını test etmemelisiniz. Kişi% 100 kod kapsamına (özel yöntemlerde) ulaşmadan yerine getirilirse, bu ölü veya işe yaramaz kod olabilir.
wuppi

207

Bu makaleden: JUnit ve SuiteRunner (Bill Venners) ile Özel Yöntemleri Test Etme , temel olarak 4 seçeneğiniz vardır:

  1. Özel yöntemleri test etmeyin.
  2. Yöntem paketine erişim verin.
  3. Yuvalanmış bir test sınıfı kullanın.
  4. Yansımayı kullanın.

@JimmyT., "Üretim kodu" kimin için bağlıdır. Hedef kullanıcılar sysadmins olan VPN içinde konuşlanmış uygulamalar için üretilen kodu üretim kodu olarak çağırırım.
Pacerier

@Pacerier Ne demek istiyorsun?
Jimmy T.

1
5. seçenek, yukarıda belirtildiği gibi özel yöntemi çağıran genel yöntemi test mi? Doğru?
user2441441

1
@LorenzoLerate Bununla güreştim çünkü gömülü geliştirme yapıyorum ve yazılımımın olabildiğince küçük olmasını istiyorum. Düşünebilmemin tek nedeni boyut kısıtlamaları ve performans.

Yansıma kullanarak testi gerçekten seviyorum: Burada desen yöntemi yöntemi = targetClass.getDeclaredMethod (methodName, argClasses); method.setAccessible (doğru); return method.invoke (targetObject, argObjects);
Aguid

127

Genellikle bir birim testi, bir sınıfın veya birimin genel arayüzünü kullanmak için tasarlanmıştır. Bu nedenle, özel yöntemler, açıkça test etmeyi beklemediğiniz uygulama ayrıntısıdır.


16
Bu en iyi cevap IMO'dur, ya da genellikle söylendiği gibi, yöntemleri değil test davranışını test eder. Birim testi, kaynak kodu metriklerinin, statik kod analiz araçlarının ve kod incelemelerinin yerine geçmez. Özel yöntemler testleri ayırmak zorunda kalacak kadar karmaşıksa, muhtemelen daha fazla test yapılmasına değil, yeniden düzenlenmesi gerekir.
Dan Haynes

14
özel yöntemler yazmayın, sadece 10 küçük sınıf yaratın
jilet

1
Hayır, temel olarak özel yöntemlerin işlevlerini, onları çağıran genel yöntemi kullanarak test edebileceğiniz anlamına gelir.
Akshat Sharda

66

Özel bir yöntemi test etmek istediğim noktalara sadece iki örnek:

  1. Şifre çözme rutinleri - Sadece test uğruna görmek için onları kimseye görünür kılmak istemem, aksi takdirde kimse şifresini çözmek için kullanabilir. Ancak koda içseldir, karmaşıktır ve her zaman çalışması gerekir (bariz istisna, SecurityManagerbunu önlemek için yapılandırılmadığında çoğu durumda özel yöntemleri bile görüntülemek için kullanılabilen yansımadır ).
  2. Topluluk tüketimi için bir SDK oluşturma . Burada kamu tamamen farklı bir anlam kazanır, çünkü bu tüm dünyanın görebileceği bir koddur (sadece uygulamamın içinde değil). SDK kullanıcılarının görmesini istemiyorsam özel yöntemlere kod koydum - Bunu kod kokusu olarak görmüyorum, sadece SDK programlama nasıl çalışıyor. Ama elbette hala özel yöntemlerimi test etmem gerekiyor ve SDK'mın işlevselliğinin gerçekte yaşadığı yer bunlar.

Sadece "sözleşmeyi" test etme fikrini anlıyorum. Ama aslında kod test değil savunucu göremiyorum - Kilometre değişebilir.

Yani benim ödünleşim, güvenlik ve SDK'mdan ödün vermek yerine JUnits'i yansıma ile karmaşıklaştırmayı içeriyor.


5
Bir yöntemi sadece sınamak için herkese açık privateyapmamalısınız, tek alternatif bu değildir. Erişim değiştirici yoktur package privateve birim testiniz aynı pakette kaldığı sürece birimi test edebileceğiniz anlamına gelir.
ngreen

1
Cevabınız hakkında yorum yapıyordum, özellikle 1. nokta, OP değil. Bir yöntemi yalnızca herkese açık olmasını istemediğiniz için özel yapmanıza gerek yoktur.
ngreen

1
@ngreen true thx - "Genel" kelimesi ile tembeldim. Cevabı herkese açık, korumalı, varsayılan (ve yansımayı belirtmek için) içerecek şekilde güncelledim. Belirtmeye çalıştığım nokta, bazı kodların gizli kalması için iyi bir neden olduğudur, ancak bu, onu test etmemizi engellememelidir.
Richard Le Mesurier

2
Herkese gerçek örnekler verdiğiniz için teşekkürler. Çoğu cevap iyidir, ancak teorik bir bakış açısıyla. Gerçek dünyada, uygulamayı sözleşmeden gizlememiz gerekir ve yine de test etmemiz gerekir. Tüm bunları okurken, belki de bunun farklı bir isimle
yepyeni

1
Bir örnek daha - Tarayıcı HTML ayrıştırıcıları . Bir html sayfasının tamamını etki alanı nesnelerine ayrıştırmak, yapının küçük bölümlerini doğrulamak için bir ton azotlu mantık gerektirir. Bunu yöntemlere ayırmak ve tek bir genel yöntemden çağırmak mantıklıdır, ancak onu oluşturan tüm küçük yöntemlerin herkese açık olmasını sağlamak da mantıklı değildir ve HTML sayfası başına 10 sınıf oluşturmak da fazla bir anlam ifade etmez.
Nether

59

Özel yöntemler genel bir yöntemle çağrılır, bu nedenle genel yöntemlerinizin girdileri de bu genel yöntemlerle çağrılan özel yöntemleri test etmelidir. Genel bir yöntem başarısız olduğunda, bu özel yöntemde bir hata olabilir.


Asıl gerçek. Sorun, uygulamanın daha karmaşık olduğu durumlarda, örneğin bir ortak yöntem birkaç özel yöntem çağırıyormuş gibi. Hangisi başarısız oldu? Ya da karmaşık bir özel
yöntemse,

Özel yöntemin neden test edilmesi gerektiğini daha önce söylemiştiniz. "O zaman bu olabilir" dedin, bu yüzden emin değilsin. IMO, bir yöntemin test edilip edilmeyeceği erişim seviyesine diktir.
Wei Qiu

39

Kullandığım başka bir yaklaşım, özel veya korumalı paketlemek için özel bir yöntemi değiştirmek ve ardından Google Guava kütüphanesinin @VisibleForTesting ek açıklamasıyla tamamlamaktır .

Bu, bu yöntemi kullanan herkese dikkat etmesini ve bir pakette bile doğrudan erişmemesini söyler. Ayrıca, bir test sınıfının fiziksel olarak aynı pakette değil, test klasörü altındaki aynı pakette olması gerekir .

Örneğin, test edilecek bir yöntem varsa src/main/java/mypackage/MyClass.javatest çağrınızın yapılması gerekir src/test/java/mypackage/MyClassTest.java. Bu şekilde, test sınıfınızdaki test yöntemine erişebilirsiniz.


1
Bunu bilmiyordum, interresting, hala böyle bir bildirime ihtiyacınız varsa tasarım sorunlarınız olduğunu düşünüyorum.
Seb

2
Bana göre, bu, yangın tatbikatını test etmek için yangın müdürünüze bir kerelik bir anahtar vermek yerine, ön kapınızı kapı üzerinde, "test için kilidi açıldı - firmamız, lütfen girmeyin ".
Fr Jeremy Krieg

@FrJeremyKrieg - bu ne anlama geliyor?
MasterJoe2

1
@ MasterJoe2: Yangın testinin amacı, binanızın yangın riskine karşı güvenliğini artırmaktır. Ama müdürün binanýza girmesine izin vermelisin. Ön kapağı kalıcı olarak açık bırakırsanız, yetkisiz erişim riskini artırır ve daha az güvenli hale getirirsiniz . Aynı şey VisibleForTesting modeli için de geçerlidir - "yangın" riskini azaltmak için yetkisiz erişim riskini artırıyorsunuzdur. Kalıcı olarak kilidini açmadan (özel olmayan) bırakmak yerine yalnızca ihtiyacınız olduğunda (örn. Yansıma kullanarak) test için bir kerelik erişim vermek daha iyidir.
Fr Jeremy Krieg

34

Eski kodu büyük ve ilginç sınıflarla test etmek için, şu anda yazdığım tek bir özel (veya genel) yöntemi test edebilmek genellikle çok yararlıdır .

Java için junitx.util.PrivateAccessor -package kullanıyorum . Özel yöntemlere ve özel alanlara erişmek için çok sayıda yararlı tek gömlek.

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

2
Kaynak Forge-Önerilen Proje PrivateAccessor değil , tüm JUnit-addons ( sourceforge.net/projects/junit-addons ) paketini indirdiğinizden emin olun .
skia.heliou

2
Ve Classbu yöntemlerde ilk parametreniz olarak a kullandığınızda , yalnızca staticüyelere eriştiğinizden emin olun .
skia.heliou

31

Gelen Bahar Framework bu yöntemi kullanarak özel yöntemler test edebilirsiniz:

ReflectionTestUtils.invokeMethod()

Örneğin:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

Şimdiye kadarki en temiz ve en özlü çözüm, ancak sadece Spring kullanıyorsanız.
Denis Nikolaenko

28

Cem Catikkas'ın çözümünü Java için yansıma kullanarak denedikten sonra, burada tarif ettiğimden daha zarif bir çözüm olduğunu söylemeliyim. Ancak, yansıma kullanmak için bir alternatif arıyorsanız ve test ettiğiniz kaynağa erişiminiz varsa, bu yine de bir seçenek olacaktır.

Herhangi bir kod yazmadan önce küçük testler tasarlamak istediğiniz bir sınıfın özel yöntemlerini, özellikle test odaklı geliştirmeyi test etmenin olası bir değeri vardır .

Özel üyelere ve yöntemlere erişerek bir test oluşturmak, yalnızca genel yöntemlere erişimle hedeflenmesi zor olan kod alanlarını test edebilir. Herkese açık bir yöntemin birkaç adımı varsa, daha sonra tek tek test edilebilen birkaç özel yöntemden oluşabilir.

Avantajları:

  • Daha ince bir ayrıntıya kadar test edebilir

Dezavantajları:

  • Test kodu, bakımı daha zor olabilecek kaynak koduyla aynı dosyada bulunmalıdır
  • .Class çıktı dosyalarıyla aynı şekilde, kaynak kodda belirtilenle aynı pakette kalmaları gerekir

Bununla birlikte, sürekli test bu yöntemi gerektiriyorsa, geleneksel, kamuya açık bir şekilde test edilebilecek özel yöntemlerin çıkarılması gerektiğinin bir işareti olabilir.

İşte bunun nasıl çalışacağına dair karmaşık bir örnek:

// Import statements and package declarations

public class ClassToTest
{
    private int decrement(int toDecrement) {
        toDecrement--;
        return toDecrement;
    }

    // Constructor and the rest of the class

    public static class StaticInnerTest extends TestCase
    {
        public StaticInnerTest(){
            super();
        }

        public void testDecrement(){
            int number = 10;
            ClassToTest toTest= new ClassToTest();
            int decremented = toTest.decrement(number);
            assertEquals(9, decremented);
        }

        public static void main(String[] args) {
            junit.textui.TestRunner.run(StaticInnerTest.class);
        }
    }
}

İç sınıf derlenecekti ClassToTest$StaticInnerTest.

Ayrıca bakınız: Java İpucu 106: Eğlence ve kâr için statik iç sınıflar


26

Diğerlerinin söylediği gibi ... özel yöntemleri doğrudan test etmeyin. İşte birkaç düşünce:

  1. Tüm yöntemleri küçük ve odaklanmış tutun (test edilmesi kolay, yanlış olanı bulmak kolay)
  2. Kod kapsamı araçlarını kullanın. Cobertura'yı seviyorum (oh mutlu günler, yeni bir sürüm çıktı gibi görünüyor!)

Ünite testlerinde kod kapsamını çalıştırın. Yöntemlerin tam olarak test edilmediğini görürseniz kapsamı artırmak için testlere ekleyin. % 100 kod kapsamını hedefleyin, ancak muhtemelen alamayacağınızı unutmayın.


Kod kapsamı için hazır. Özel yöntemde ne tür bir mantık olursa olsun, yine de bu mantığı genel bir yöntemle çağırıyorsunuz. Bir kod kapsamı aracı, hangi parçaların test kapsamında olduğunu gösterebilir, bu nedenle özel yönteminizin test edilip edilmediğini görebilirsiniz.
coolcfan

Ben sadece kamu yöntemi ana [] olduğu sınıflar gördüm ve onlar GUI açılır artı veritabanı ve dünya çapında birkaç web sunucuları bağlayın. Söylemesi kolay "dolaylı olarak test etmeyin" ...
Audrius Meskauskas

26

Özel yöntemler herkese açıktır. Aksi takdirde, ölü kodlardır. Bu yüzden genel yöntemi test edersiniz, genel yöntemin beklenen sonuçlarını ve böylece tükettiği özel yöntemleri iddia edersiniz.

Özel yöntemlerin test edilmesi, birim testlerinizi genel yöntemlerle çalıştırmadan önce hata ayıklama yoluyla test edilmelidir.

Ayrıca, tüm iddialarınız karşılanana kadar birim testlerinizde hata ayıklayarak, test odaklı geliştirme kullanılarak hata ayıklanabilirler.

Şahsen TDD kullanarak sınıf oluşturmanın daha iyi olduğuna inanıyorum; genel yöntem koçanlarını oluşturmak, daha sonra önceden tanımlanan tüm iddialarla birim testleri oluşturmak , böylece yöntemin beklenen sonucu siz kodlamadan önce belirlenir. Bu şekilde, birim test iddialarını sonuçlara uydurmak için yanlış yola girmezsiniz. Sınıfınız daha sonra sağlamdır ve tüm birim testleriniz geçtiğinde gereksinimleri karşılar.


2
Her ne kadar bu doğru olsa da, bazı çok karmaşık testlere yol açabilir - bir grubun hepsinden ziyade tek bir yöntemi tek seferde test etmek (Birim testi) için daha iyi bir modeldir. Korkunç bir öneri değil ama burada daha iyi cevaplar olduğunu düşünüyorum - bu son çare olmalıdır.
Bill K

24

Spring kullanıyorsanız, ReflectionTestUtils , burada en az çabayla yardımcı olan bazı kullanışlı araçlar sağlar. Örneğin, istenmeyen bir genel ayarlayıcı eklemek zorunda kalmadan özel bir üyeye alay etmek için:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

24

İsteksiz veya değiştiremediğiniz mevcut kodu test etmeye çalışıyorsanız, yansıma iyi bir seçimdir.

Sınıfın tasarımı hala esnekse ve ayrı ayrı test etmek istediğiniz karmaşık bir özel yönteminiz varsa, onu ayrı bir sınıfa çıkarmanızı ve bu sınıfı ayrı ayrı test etmenizi öneririm. Bunun orijinal sınıfın genel arayüzünü değiştirmesi gerekmez; dahili olarak helper sınıfının bir örneğini oluşturabilir ve helper yöntemini çağırabilir.

Yardımcı yöntemden gelen zor hata koşullarını test etmek istiyorsanız, bir adım daha ileri gidebilirsiniz. Yardımcı sınıftan bir arabirim ayıklayın, yardımcı sınıfı (arabirimi aracılığıyla kullanılır) enjekte etmek için orijinal sınıfa bir ortak alıcı ve ayarlayıcı ekleyin ve ardından özgün sınıfın nasıl sahte bir sürümünü orijinal sınıfın içine sınayın. yardımcıdan istisnalara cevap verir. Bu yaklaşım, yardımcı sınıfı test etmeden orijinal sınıfı test etmek istiyorsanız da yararlıdır.


19

Özel yöntemleri test etmek, dahili uygulamayı her değiştirdiğinizde istemci kodunu kırdığınızda (bu durumda testler) sınıfınızın kapsüllenmesini bozar.

Bu yüzden özel yöntemleri test etmeyin.


4
birim testi ve src kodu bir çifttir. Src'yi değiştirirseniz, birim testini değiştirmeniz gerekebilir. Junit testi duygusu budur. Tüm çalışmaların eskisi gibi çalışacağından emin olurlar. ve kodu değiştirirseniz kırılırlarsa sorun olmaz.
AlexWien

1
Kod değişirse birim testinin değişmesi gerektiğini kabul etmeyin. Sınıfın mevcut işlevselliğini değiştirmeden sınıfa işlevsellik eklemeniz istenirse, kodunuzu değiştirdikten sonra bile mevcut birim testleri geçmelidir. Peter'ın belirttiği gibi, birim testleri arayüzün nasıl test edileceğini değil, arayüzü test etmelidir. Test Odaklı Geliştirme bölümünde, testler kodun yazılmasından önce oluşturulur, sınıfın arayüzüne odaklanır, dahili olarak nasıl çözüldüğüne değil.
Harald Coppoolse

16

JUnit.org SSS sayfasından cevap :

Ama eğer ...

JDK 1.3 veya üstünü kullanıyorsanız, erişim kontrol mekanizmasını PrivilegedAccessor yardımıyla değiştirmek için yansımayı kullanabilirsiniz . Nasıl kullanılacağı hakkında ayrıntılar için bu makaleyi okuyun .

JDK 1.6 veya üstünü kullanıyorsanız ve @Test ile testlerinize açıklama ekliyorsanız, test yöntemlerinize yansıma eklemek için Dp4j'yi kullanabilirsiniz . Nasıl kullanılacağı hakkında ayrıntılar için bu test komut dosyasına bakın .

PS: Ben Dp4j ana katkıda , ben yardıma ihtiyacınız olup olmadığını sor . :)


16

Kodu değiştiremeyeceğiniz eski bir uygulamanın özel yöntemlerini test etmek isterseniz, Java için bir seçenek , sınıfa özel olduklarında bile bir nesneye alay oluşturmanıza izin veren jMockit'dir .


4
Jmockit kütüphanesinin bir parçası olarak, özel yöntemleri test etmeyi kolaylaştıran Deencapsulation sınıfına erişebilirsiniz: Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Domenic D.

Bu yaklaşımı her zaman kullanıyorum. Çok yararlı
MedicineMan

14

Özel yöntemleri test etmeme eğilimindeyim. Delilik yatıyor. Şahsen, sadece halka açık arayüzlerinizi test etmeniz gerektiğine inanıyorum (ve korumalı ve dahili yöntemleri içerir).


14

JUnit kullanıyorsanız, junit-eklentilerine bir göz atın . Java güvenlik modelini yoksayma ve özel yöntemlere ve özniteliklere erişme yeteneğine sahiptir.


13

Kodunuzu biraz yeniden düzenlemenizi öneririm. Sadece kodunuzu test etmek için yansıma veya başka tür şeyler kullanmayı düşünmeniz gerektiğinde, kodunuzla ilgili bir sorun var.

Farklı sorun türlerinden bahsettiniz. Özel alanlarla başlayalım. Özel alanlar durumunda, yeni bir kurucu ekledim ve buna enjekte edilen alanlar ekleyecektim. Bunun yerine:

public class ClassToTest {

    private final String first = "first";
    private final List<String> second = new ArrayList<>();
    ...
}

Bunu kullanırdım:

public class ClassToTest {

    private final String first;
    private final List<String> second;

    public ClassToTest() {
        this("first", new ArrayList<>());
    }

    public ClassToTest(final String first, final List<String> second) {
        this.first = first;
        this.second = second;
    }
    ...
}

Bazı eski kodlarda bile bu sorun olmaz. Eski kod boş bir kurucu kullanacak ve bana sorarsanız, yeniden düzenlenmiş kod daha temiz görünecek ve yansıma olmadan testte gerekli değerleri enjekte edebileceksiniz.

Şimdi özel yöntemler hakkında. Kişisel deneyimlerime göre, test için özel bir yöntemi saplamak zorunda kaldığınızda, o yöntemin o sınıfta bir ilgisi yoktur. Ortak bir desen, bu durumda, bir arayüz içine sarmak gibi olur Callableve daha sonra bu arayüze yapıcıda da (bu çoklu kurucu hile ile) geçersiniz:

public ClassToTest() {
    this(...);
}

public ClassToTest(final Callable<T> privateMethodLogic) {
    this.privateMethodLogic = privateMethodLogic;
}

Çoğunlukla yazdığım tek şey bağımlılık enjeksiyon modeli gibi görünüyor. Kişisel deneyimlerime göre test ederken bu gerçekten kullanışlı ve bence bu tür kodlar daha temiz ve bakımı daha kolay olacak. Aynı şeyi iç içe dersler için de söyleyebilirim. Yuvalanmış bir sınıf ağır mantık içeriyorsa, onu bir paket özel sınıfı olarak taşıdıysanız ve buna ihtiyaç duyan bir sınıfa enjekte etseniz daha iyi olur.

Ayrıca eski kodu yeniden düzenlerken ve korurken kullandığım birkaç tasarım deseni daha var, ancak hepsi test etmek için kodunuzun durumlarına bağlı. Yansımayı kullanmak çoğunlukla bir sorun değildir, ancak yoğun bir şekilde test edilen ve her dağıtımdan önce testler yapılan bir kurumsal uygulamanız olduğunda her şey gerçekten yavaşlar (sadece sinir bozucu ve bu tür şeyleri sevmiyorum).

Ayrıca setter enjeksiyonu var, ama kullanmanızı tavsiye etmem. Bir kurucuya bağlı kalsam ve gerçekten gerekli olduğunda her şeyi başlatırsam, gerekli bağımlılıkları enjekte etme olasılığını bırakırsam.


1
ClassToTest(Callable)Enjeksiyon ile aynı fikirde değil . Bu ClassToTestdaha karmaşık hale getirir . Basit tutun. Ayrıca, bu başka birinin patronu olması gereken bir ClassToTestşey söylemesini gerektirir ClassToTest. Mantık enjekte etmek için bir yer var, ama bu o değil. Sınıfın bakımını zorlaştırdınız, daha kolay değil.
Loduwijk

Ayrıca, test yönteminizin daha fazla soruna neden olabileceği noktaya kadar karmaşıklığı Xarttırmaması tercih edilir X... ve bu nedenle ek testler gerektirir ... ki daha fazla soruna neden olabilecek bir şekilde uyguladıysanız .. (bu sonsuz bir döngü değildir; her yineleme muhtemelen bir öncekinden daha küçüktür, ancak yine de can sıkıcıdır)
Loduwijk

2
Sınıfın neden ClassToTestsürdürülmesini zorlaştırdığını açıklayabilir misiniz ? Aslında uygulamanızın bakımını kolaylaştırır, ihtiyacınız olan her farklı değer firstve 'ikinci' değişken için yeni sınıf oluşturmayı ne önerirsiniz ?
GROX13

1
Test yöntemi karmaşıklığı artırmaz. Sadece sınıfınız kötü yazılmış, o kadar zayıf ki test edilemez.
GROX13

Bakımı daha zor çünkü sınıf daha karmaşık. class A{ A(){} f(){} }daha basit class A { Logic f; A(logic_f) } class B { g() { new A( logic_f) } }. Bu doğru olmasaydı ve bir yapıcı argümanları olarak bir sınıf mantığı sağlamanın daha kolay olduğu doğru olsaydı, tüm mantığı yapıcı argümanları olarak geçirirdik. class B{ g() { new A( you_dont_know_your_own_logic_but_I_do ) } }A'nın bakımını nasıl kolaylaştırdığını iddia edebileceğinizi görmüyorum . Böyle enjekte
etmenin

12

Özel bir yönteme sadece aynı sınıftan erişilmelidir. Dolayısıyla, herhangi bir test sınıfından bir hedef sınıfın “özel” yöntemini test etmenin bir yolu yoktur. Bir çıkış yöntemi, manuel olarak birim testi yapabilmeniz veya yönteminizi “özel” den “korumalı” olarak değiştirebilmenizdir.

Ve sonra korumalı bir yönteme yalnızca sınıfın tanımlandığı pakette erişilebilir. Dolayısıyla, hedef sınıfın korumalı bir yöntemini test etmek, test sınıfınızı hedef sınıfla aynı pakette tanımlamamız gerektiği anlamına gelir.

Yukarıdakilerin tümü gereksiniminize uygun değilse , özel yönteme erişmek için yansıma yolunu kullanın.


"Korumalı" kelimeyi "arkadaşça" ile karıştırıyorsunuz. Korumalı bir yönteme yalnızca nesneleri hedef sınıfa atanabilen bir sınıf (yani alt sınıflar) tarafından erişilebilir.
Sayo Oladeji

1
Aslında Java'da "Dost" yoktur, terim "Paket" dir ve özel / genel / korumalı değiştiricinin olmaması ile gösterilir. Bu yanıtı düzeltmiş olurdum ama bunu zaten söyleyen gerçekten iyi bir yanıt var - bu yüzden sadece silmenizi tavsiye ederim.
Bill K

"Bu cevap sadece yanlış." Ben cevabın geri kalanıyla çelişen son cümleye ulaşıncaya kadar. Son cümle ilk olmalı.
Loduwijk

11

Yukarıda belirtildiği gibi, iyi bir yol onları genel arayüzleriniz aracılığıyla test etmektir.

Bunu yaparsanız, özel yöntemlerinizin gerçekten testlerinizden yürütülüp yürütülmediğini görmek için bir kod kapsamı aracı (Emma gibi) kullanmak iyi bir fikirdir.


Dolaylı olarak test etmemelisiniz! Sadece kapsama yoluyla dokunmakla kalmaz; beklenen sonucun teslim edildiğini test edin!
AlexWien

11

İşte özel alanları test etmek için benim genel işlevi:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

10

Örnek için lütfen aşağıya bakın;

Aşağıdaki içe aktarma ifadesi eklenmelidir:

import org.powermock.reflect.Whitebox;

Şimdi özel yöntem, çağrılacak yöntem adı ve ek parametreleri içeren nesneyi doğrudan aşağıdaki gibi aktarabilirsiniz.

Whitebox.invokeMethod(obj, "privateMethod", "param1");

10

Bugün, özel yöntemlerin ve alanların test edilmesine yardımcı olmak için bir Java kütüphanesi ittim. Android göz önünde bulundurularak tasarlanmıştır, ancak herhangi bir Java projesi için gerçekten kullanılabilir.

Özel yöntemlerle veya alanlarla veya kurucularla bazı kodlarınız varsa, BoundBox'ı kullanabilirsiniz . Tam olarak aradığınızı yapar. Aşağıda, bir Android etkinliğinin test edilmesi için iki özel alanına erişen bir test örneği verilmiştir:

@UiThreadTest
public void testCompute() {

    // Given
    boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());

    // When
    boundBoxOfMainActivity.boundBox_getButtonMain().performClick();

    // Then
    assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}

BoundBox özel / korumalı alanları, yöntemleri ve yapıcıları test etmeyi kolaylaştırır. Kalıtımla gizlenen öğelere bile erişebilirsiniz. Gerçekten de, BoundBox kapsüllemeyi keser. Her şeye yansıma yoluyla erişmenizi sağlar, ancak her şey derleme zamanında kontrol edilir.

Bazı eski kodları test etmek için idealdir. Dikkatlice kullanın. ;)

https://github.com/stephanenicolas/boundbox


1
Sadece denedim, BoundBox basit ve zarif ve çözümdür!
forhas

9

İlk olarak, şu soruyu atacağım: Özel üyelerinizin neden izole testlere ihtiyacı var? Bunlar, kamu yüzeyinden ayrı olarak test edilmeyi gerektirecek karmaşık davranışlar sağlayan karmaşık mı? 'Kod satırı' testi değil, birim testi. Küçük şeyleri terlemeyin.

Eğer bu kadar büyük, bu özel üyelerin her biri karmaşık bir 'birim' olacak kadar büyüklerse - bu tür özel üyeleri bu sınıftan yeniden düzenlemeyi düşünün.

Yeniden düzenleme uygun değilse veya mümkün değilse, birim sınaması sırasında bu özel üye işlevlerine / üye sınıflarına erişimi değiştirmek için strateji modelini kullanabilir misiniz? Birim sınaması altında, strateji ek doğrulama sağlar, ancak sürüm derlemelerinde bu basit geçiş olacaktır.


3
Çünkü genellikle genel bir yöntemden belirli bir kod parçası dahili bir özel yönteme yeniden yansıtılır ve gerçekten yanlış yapmış olabileceğiniz kritik mantık parçasıdır. Bunu herkese açık yöntemden bağımsız olarak test etmek istiyorsunuz
oxbow_lakes

Bazen birim testi olmadan en kısa kod bile doğru değildir. Sadece 2 jeografik açı arasındaki farkı hesaplamaya çalışın. 4 satır kod ve çoğu ilk denemede doğru yapmayacak. Bu tür yöntemler birim testine ihtiyaç duyar, çünkü form güvenilir bir kodun temelini oluşturur. (Ans gibi yararlı kodlar da herkese açık olabilir; daha az yararlıdır
AlexWien

8

Son zamanlarda bu sorun vardı ve açıkça Java yansıma API, iki örnek kullanma sorunları önler Picklock adlı küçük bir araç yazdı :

Arama yöntemleri, örneğin private void method(String s)- Java yansıması ile

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

Arama yöntemleri, örneğin private void method(String s)- Picklock tarafından

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

Ayar alanları, örneğin private BigInteger amount;- Java yansıması ile

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

Ayar alanları, örneğin private BigInteger amount;- Picklock tarafından

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));

7

PowerMockito bunun için üretildi. Maven bağımlılığını kullan

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-mockito-release-full</artifactId>
    <version>1.6.4</version>
</dependency>

Sonra yapabilirsin

import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);
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.