Java projesi üzerinde çalışıyorum. Birim testinde yeniyim. Java sınıflarında özel yöntemleri test etmenin en iyi yolu nedir?
Java projesi üzerinde çalışıyorum. Birim testinde yeniyim. Java sınıflarında özel yöntemleri test etmenin en iyi yolu nedir?
Yanıtlar:
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.
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ı.
Ö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.
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.
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/ )
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.
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.
Özel bir yöntemi gerçekten test etmeniz gerekiyorsa, Java ile demek istediğim, fest assert
ve / 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.
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.
protected
genel API’nin bir parçasıdır ve aynı sınırlamalara tabidir (asla değiştirilemez, belgelendirilmemeli, ...). C # internal
ile birlikte kullanabilirsiniz InternalsVisibleTo
.
Ü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