Özel yöntemleri nasıl test edersiniz?


184

Java projesi üzerinde çalışıyorum. Birim testinde yeniyim. Java sınıflarında özel yöntemleri test etmenin en iyi yolu nedir?


1
StackOverflow'ta bu soruyu kontrol edin. Birkaç teknikten bahsedilir ve tartışılır. Özel yöntemleri test etmenin en iyi yolu nedir?
Chiron

34
Kanımca her zaman, özel yöntemlerin mevcut olanları test etmeniz gerektiği gibi test edilmesi gerekmediği olmuştur. Genel bir yöntem. Genel yöntemi kıramazsanız, özel yöntemlerin ne yaptığı gerçekten önemli mi?
Rig

1
Hem genel hem de özel yöntemler test edilmelidir. Bu nedenle, bir test sürücüsünün genellikle test ettiği sınıfın içinde olması gerekir. gibi bu .
aşırı değişim

2
@Rig - +1 - Özel bir yöntemin tüm gerekli davranışlarını herkese açık yöntemlerden çağırabiliyor olmalısınız - eğer yapamazsanız, işlevsellik asla çağrılamaz, bu nedenle test etmenin bir anlamı yoktur.
James Anderson,

Kullanım @Jailbreakgelen Manifold doğrudan özel yöntemlerine erişmek için çerçeve. Bu şekilde test kodunuz güvenli ve okunabilir durumda kalır . Her şeyden önce, hiçbir tasarım ödün vermez, test uğruna aşırı maruz kalma yöntemleri ve alanları yoktur.
Scott,

Yanıtlar:


239

Genelde, özel yöntemleri doğrudan test etmiyorsunuz. Özel oldukları için, onları bir uygulama detayı olarak kabul edin. Hiç kimse onlardan birini arayacak ve belirli bir şekilde çalışmasını beklemeyecek.

Bunun yerine genel arayüzünüzü test etmelisiniz. Özel yöntemlerinizi çağıran yöntemler beklediğiniz gibi çalışıyorsa, özel yöntemlerin doğru çalıştığını ekleyerek varsayalım.


39
+1 bazilyon. Ve eğer özel bir yöntem hiç çağrılmazsa, ünite testi yapmayın, silin!
Steven A. Lowe

246
Katılmıyorum. Bazen özel bir yöntem sadece bir uygulama detayıdır, ancak doğru çalıştığından emin olmak için test etmeyi garanti edecek kadar karmaşıktır. Genel arayüz, bu özel algoritmayı doğrudan hedef alan bir test yazmak için çok yüksek bir soyutlama seviyesi sunuyor olabilir. Paylaşılan veriler nedeniyle bunu ayrı bir sınıfa ayırmak her zaman mümkün değildir. Bu durumda özel bir yöntemi test etmenin doğru olduğunu söyleyebilirim.
quant_dev

10
Elbette silin. Kullanmadığınız bir işlevi silmemenin tek olası nedeni, ileride ihtiyaç duyabileceğinizi düşünüyorsanız ve o zaman bile silmelisiniz ancak işlem kayıtlarınızdaki işlevi not edin veya en azından yorum yapın. insanlar görmezden gelebilecekleri fazladan şeyler olduğunu biliyorlar.
44'te jhocking

38
@quant_dev: Bazen bir sınıfın başka birkaç sınıfa da yeniden yerleştirilmesi gerekir . Paylaşılan verileriniz de ayrı bir sınıfa çekilebilir. Diyelim ki, "bağlam" sınıfı. O zaman iki yeni sınıfınız paylaşılan verileri için bağlam sınıfına başvurabilir. Bu mantıksız görünebilir, ancak özel yöntemleriniz bireysel testlere ihtiyaç duyacak kadar karmaşıksa, nesne grafiğinizin biraz daha ayrıntılı olması gerektiğini belirten bir kod kokusudur.
Phil,

43
Bu cevap ETMEZ soruya cevap. Soru nasıl özel yöntemler değil, test edilmelidir olsun onlar test edilmelidir. Özel bir yöntemin test edilip edilmemesi ilginç bir soru ve tartışmaya değer, ancak burada değil . Uygun yanıt, özel yöntemleri test etmenin iyi bir fikir olmayabileceğini ve meseleye daha derinden girecek ayrı bir soruya link verebileceğini belirten bir yorum eklemek.
TallGuy

118

Genelde bundan kaçınırdım. Özel yönteminiz ayrı bir birim testine ihtiyaç duyacak kadar karmaşıksa, genellikle kendi sınıfını hak ettiği anlamına gelir. Bu, tekrar kullanılabilir bir şekilde yazmaya teşvik edebilir. Daha sonra yeni sınıfı test etmeli ve bunun ortak arayüzünü eski sınıfınızda aramalısınız.

Öte yandan, bazen uygulama ayrıntılarını ayrı sınıflara ayırmak, karmaşık arayüzleri olan sınıflara, eski ve yeni sınıf arasında geçen birçok veriye veya OOP bakış açısından iyi görünebilecek bir tasarıma yol açar. sorun alanından gelen sezgileri eşleştirin (örneğin, özel yöntemleri test etmekten kaçınmak için bir fiyatlandırma modelini iki parçaya bölmek çok sezgisel değildir ve daha sonra kodu korurken / uzatırken sorunlara yol açabilir). Her zaman birlikte değiştirilen “ikiz sınıflara” sahip olmak istemezsiniz.

Kapsülleme ve test edilebilirlik arasında bir seçim yapıldığında, ikincisini tercih ederim. Doğru bir şekilde test edilmemiş olması, doğru çalışmaması ve doğru şekilde test edilmemesi nedeniyle, doğru bir kodun (yani doğru çıktıların üretilmesi) daha iyi olması önemlidir. Java'da, yalnızca "default" yöntemine erişebilir ve birim testini aynı pakete koyabilirsiniz. Birim testleri, geliştirdiğiniz paketin bir parçasıdır ve testler ile test edilen kod arasında bir bağımlılık olması uygundur. Bu, uygulamayı değiştirdiğinizde, testlerinizi değiştirmeniz gerekebileceği anlamına gelir, ancak sorun değil - uygulamanın her değişikliği kodun yeniden test edilmesini gerektirir ve testlerin bunu yapmak için değiştirilmesi gerekiyorsa, o zaman sadece o.

Genel olarak, bir sınıf birden fazla arayüz sunabilir. Kullanıcılar için bir arayüz ve bakıcılar için bir arayüz var. İkincisi, kodun uygun şekilde test edilmesini sağlamak için daha fazla gösterebilir. Özel bir yöntem üzerinde bir birim testi olması gerekmez - örneğin, günlük kaydı olabilir. Günlük kaydı da "enkapsülasyonu sonlandırır", fakat yine de yapıyoruz, çünkü çok faydalı.


9
+1 İlginç nokta. Sonunda, sürdürülebilirlik ile ilgili, iyi OOP’un “kurallarına” uymak değil.
Phil

9
+1 Kapsülleme konusunda test edilebilirliğe tamamen katılıyorum.
Richard

31

Özel yöntemlerin test edilmesi karmaşıklıklarına bağlı olacaktır; Bazı özel hat metotları gerçekten fazladan test etme zorunluluğunu garanti etmeyebilir (buna kamu metotları da söylenebilir), fakat bazı özel metotlar genel metotlar kadar karmaşık olabilir ve genel arayüz üzerinden test etmek zor olabilir.

Tercih ettiğim teknik, aynı paket içindeki bir birim testine erişime izin verecek, ancak yine de diğer tüm kodlardan kapsüllenmiş olacak şekilde özel yöntem paketini özel yapmaktır. Bu, (muhtemelen) karmaşık mantığın tüm bölümlerini kapsayan bir genel yöntem testine güvenmek yerine doğrudan özel yöntem mantığını test etme avantajını sağlayacaktır.

Bu, Google Guava kütüphanesindeki @VisibleForTesting ek açıklamasıyla eşleştirilmişse, bu paket özel yöntemini yalnızca testler için görünür olarak açıkça işaretlersiniz ve bu nedenle başka bir sınıf tarafından çağrılmaması gerekir.

Bu tekniğin karşıtları bunun kapsüllemeyi kıracağını ve aynı pakette kodlamak için özel yöntemler açacağını savunuyorlar. Bunun, kapsüllemeyi bozduğunu ve diğer sınıflara özel kod açtığını kabul ederken, karmaşık mantığın test edilmesinin katı kapsüllemeden daha önemli olduğunu ve test için açıkça görülebilen açıkça işaretlenmiş paket özel yöntemlerin kullanılmamasının sadece geliştiricilerin sorumluluğunda olması gerektiğini düşünüyorum. kod tabanını kullanarak ve değiştirerek.

Testten önce özel yöntem:

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

Paket özel yöntem test için hazır:

@VisibleForTesting
int add(int a, int b){
    return a + b;
}

Not: Testleri aynı pakete koymak, aynı fiziksel klasöre koymakla aynı değildir. Ana kodunuzu ve test kodunu ayrı fiziksel klasör yapılarına ayırmak genel olarak iyi bir uygulamadır, ancak bu teknik, sınıflar aynı pakette tanımlandığı sürece çalışacaktır.


14

Harici API'ler kullanamıyorsanız veya istemiyorsanız, yansıtmayı kullanarak özel yöntemlere erişmek için hala standart JDK API kullanabilirsiniz. İşte bir örnek

MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);

Java Eğitimi http://docs.oracle.com/javase/tutorial/reflect/ veya Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary adresini ziyaret edin. Daha fazla bilgi için html .

@Kij'in cevabına değindiği gibi, yansıma kullanan basit bir çözümün özel bir yöntemi test etmek için gerçekten iyi olduğu zamanlar vardır.


9

Birim test durumu, kod biriminin test edilmesi anlamına gelir. Bu, arayüzü test etmek anlamına gelmez, çünkü arayüzü test ediyorsanız, kod birimini test ettiğiniz anlamına gelmez. Bir tür kara kutu testi haline geldi. Ayrıca, en küçük birim seviyesindeki sorunları bulmak, arayüz seviyesindeki sorunları belirlemek ve daha sonra hangi parçanın çalışmadığı konusunda hata ayıklamaya çalışmaktan daha iyidir. Bu nedenle, birim test durumu kapsamlarına bakılmaksızın test edilmelidir. Aşağıdaki özel yöntemleri test etmek için bir yoldur.

Java kullanıyorsanız, test edilen sınıfın herhangi bir özel yöntemini çağırmak için Deencapsulation.invoke öğesini sağlayan jmockit kullanabilirsiniz. Sonunda çağırmak için yansıma kullanır, ancak etrafına güzel bir sarıcı sağlar. ( https://code.google.com/p/jmockit/ )


Bu soruyu soruyu nasıl cevaplıyor?
gnat

@gnat, Soru şuydu: Java sınıflarında özel yöntemleri test etmenin en iyi yolu nedir? Ve ilk noktam soruyu cevaplıyor.
agrawalankur

ilk noktanız sadece bir araç tanıtmaktır, ancak neden özel araçlardan daha iyi olduğuna inandığınızı açıklamamaktadır; örneğin, özel yöntemleri dolaylı olarak
gnat

jmockit taşındı ve eski resmi sayfa "Sentetik İdrar ve Yapay Çiş" ile ilgili bir makaleye işaret ediyor. Wich bu arada hala alay konusu şeyler ile ilgili, ancak burada ilgili değil :) Yeni resmi sayfa: jmockit.github.io
Guillaume

8

Her şeyden önce, diğer yazarların önerdiği gibi: özel yöntemi denemek için gerçekten ihtiyacınız varsa, iki kez düşünün. Ve öyle olsa bile, ...

.NET'te "Internal" yöntemine dönüştürebilir ve birim test projeniz için " InternalVisible " paketini yapabilirsiniz .

Java'da sınava girecek sınıfta sınamalar yazabilirsiniz ve sınama yöntemleriniz de özel yöntemleri çağırabilir. Gerçekten büyük bir Java deneyimim yok, bu yüzden muhtemelen en iyi yöntem bu değil.

Teşekkürler.


7

Genelde böyle yöntemleri korurum. Diyelim ki sınıfınız içeride:

src/main/java/you/Class.java

Bir test sınıfı oluşturabilirsiniz:

src/test/java/you/TestClass.java

Artık korunan yöntemlere erişiminiz var ve bunları test edebilirsiniz (JUnit veya TestNG gerçekten önemli değil), ancak bu yöntemleri istemediğiniz arayanlardan uzak tutuyorsunuz.

Bunun bir maven tarzı kaynak ağacı beklediğini unutmayın.


3

Özel bir yöntemi gerçekten test etmeniz gerekiyorsa, Java ile demek istediğim, fest assertve / veya kullanabilirsiniz fest reflect. Yansıma kullanır.

Kütüphaneyi maven ile alın (verilen sürümler bence en son sürüm değildir) veya doğrudan sınıf yolunuza alın:

<dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-assert</artifactId>
     <version>1.4</version>
    </dependency>

    <dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-reflect</artifactId>
    <version>1.2</version>
</dependency>

Örnek olarak, 'myPrivateMethod' adında özel bir yöntemle 'MyClass' adında bir sınıfınız varsa, bir String değerini parametre olarak 'this cool test!' Olarak güncelleyen bir sınıfınız varsa, aşağıdaki junit testini yapabilirsiniz:

import static org.fest.reflect.core.Reflection.method;
...

MyClass objectToTest;

@Before
public void setUp(){
   objectToTest = new MyClass();
}

@Test
public void testPrivateMethod(){
   // GIVEN
   String myOriginalString = "toto";
   // WHEN
   method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
   // THEN
   Assert.assertEquals("this is cool testing !", myOriginalString);
}

Bu kütüphane aynı zamanda herhangi bir fasulye özelliğini (özel olmaları ve ayarlayıcıların yazılmamasına bakılmaksızın) sahte olarak değiştirmenize olanak sağlar ve bunu Mockito ile veya başka bir sahte çerçeveyle kullanmak gerçekten harikadır. Şu anda bilmeniz gereken tek şey (bunun sonraki sürümlerde daha iyi olup olmayacağını bilmeyiniz), manipüle etmek istediğiniz hedef alanın / yöntemin adı ve imzasıdır.


2
Yansıma, özel yöntemleri test etmenize olanak sağlarken, bunların kullanımlarını tespit etmek için iyi değildir. Özel bir yöntemin adını değiştirirseniz, bu testinizi kıracak.
Richard

1
Test evet kıracak, ama bu benim açımdan küçük bir konudur. Tabii ki, paket görünürlüğüyle ilgili aşağıdaki açıklamalarınız konusunda tamamen aynı fikirdeyim (ayrı klasörlerdeki test sınıfları ile ama aynı paketlerle) gerçekten seçeneğim var. Örneğin, "iyi" yöntemler uygulamamış bir işletmede (aynı pakette olmayan test sınıfları, vb.) Çok kısa bir göreviniz varsa, üzerinde çalıştığınız mevcut kodu yeniden düzenlemek için zamanınız yoksa / test, bu hala iyi bir alternatif.
kij 27:12

2

Normalde C # ile yaptığım, yöntemleri korumalı ve özel değil yapmaktır. Bu, biraz daha az bir özel erişim değiştiricisidir, ancak yöntemi, test edilen sınıftan miras almayan tüm sınıflardan gizler.

public class classUnderTest
{
   //this would normally be private
   protected void methodToTest()
   {
     //some code here
   }
}

Doğrudan classUnderTest'den devralmayan herhangi bir sınıf, methodToTest'in bile var olduğu hakkında hiçbir fikriniz yoktur. Test kodumda, bu yönteme erişim sağlayan ve genişleten özel bir test sınıfı oluşturabilirim ...

class TestingClass : classUnderTest
{
   public void methodToTest()
   {
     //this returns the method i would like to test
     return base.methodToTest();
   }
}

Bu sınıf sadece test projemde var. Tek amacı, bu tek yönteme erişim sağlamaktır. Diğer sınıfların çoğunun sahip olmadığı yerlere ulaşmamı sağlıyor.


4
protectedgenel API’nin bir parçasıdır ve aynı sınırlamalara tabidir (asla değiştirilemez, belgelendirilmemeli, ...). C # internalile birlikte kullanabilirsiniz InternalsVisibleTo.
CodesInChaos,

1

Ünite testlerinizi test ettiğiniz sınıfta bir iç sınıfa koyarsanız, özel yöntemleri kolayca test edebilirsiniz. TestNG'yi kullanarak, birim testleriniz @Test ile açıklamalı genel statik iç sınıflar olmalıdır:

@Test
public static class UserEditorTest {
    public void test_private_method() {
       assertEquals(new UserEditor().somePrivateMethod(), "success");
    }
}

İç sınıf olduğu için özel yöntem çağrılabilir.

Testlerim maven'den çalıştırılıyor ve otomatik olarak bu test durumlarını buluyor. Sadece bir sınıfı test etmek istiyorsanız,

$ mvn test -Dtest=*UserEditorTest

Kaynak: http://www.ninthavenue.com.au/how-to-unit-test-private-methods


4
Test kodunu (ve ek iç sınıfları) dağıtılan paketin gerçek koduna eklemeyi mi öneriyorsunuz?

1
Test sınıfı, test etmek istediğiniz sınıfın iç sınıfıdır. Eğer bu iç sınıfların konuşlandırılmasını istemiyorsanız, * Test.class dosyalarını paketinizden silebilirsiniz.
Roger Keays

1
Bu seçeneği sevmiyorum, ancak çoğu için muhtemelen geçerli bir çözüm, olumsuz noktalara değmez.
Bill K,

@RogerKeays: Teknik olarak bir konuşlandırma yaptığınızda, bu "iç sınıfları sınamak" nasıl kaldırılır? Lütfen onları elle kestiğini söyleme.
gyorgyabraham

Onları çıkarmam. Evet. Çalışma zamanında hiç çalıştırılmayan bir kod dağıtırım. Gerçekten bu kadar kötü.
Roger Keays
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.