Mockito kullanarak özel yöntemi test etme


104
public class A {

    public void method (boolean b) {
          eğer (b == doğru)
               Yöntem 1();
          Başka
               method2 ();
    }

    özel void yöntemi1 () {}
    özel void yöntemi2 () {}
}
public class TestA {

    @Ölçek
    public void testMethod () {
      A a = sahte (A. sınıf);
      a.method (doğru);
      // valid (a) .method1 () gibi nasıl test edilir;
    }
}

Özel yöntem nasıl test edilir veya çağrılmaz ve özel yöntem mockito kullanılarak nasıl test edilir ???


Yanıtlar:


81

Bunu Mockito ile yapamazsınız, ancak Mockito'yu genişletmek ve özel yöntemleri taklit etmek için Powermock'u kullanabilirsiniz . Powermock, Mockito'yu destekler. İşte bir örnek.


19
Bu cevapla kafam karıştı. Bu alay ediyor, Ama başlık özel yöntemleri test ediyor
diyoda_

Özel yöntemle dalga geçmek için Powermock'u kullandım, ancak özel yöntemi Powermock ile nasıl test edebilirim. Nereye, bazı girdileri iletebilir ve yöntemden bazı çıktılar bekleyebilir ve ardından çıktıyı doğrulayabilirim?
Rito

Girdi çıktısını taklit edersiniz, gerçek işlevselliği test edemezsiniz.
Talha

131

Mockito ile mümkün değil. Onların itibaren wiki

Mockito neden özel yöntemlerle dalga geçmiyor?

Birincisi, özel yöntemlerle alay etme konusunda dogmatik değiliz. Özel yöntemleri umursamıyoruz çünkü özel yöntemleri test etme açısından mevcut değil. Mockito'nun özel yöntemlerle dalga geçmemesinin birkaç nedeni:

Asla kurşun geçirmez olmayan sınıf yükleyicilerin hacklenmesini gerektirir ve api'yi değiştirir (özel test çalıştırıcısı kullanmalı, sınıfa açıklama eklemelisiniz, vb.).

Etrafında çalışmak çok kolaydır - sadece yöntemin görünürlüğünü özelden paket korumalı (veya korumalı) olarak değiştirin.

Bunu uygulamak ve sürdürmek için zaman harcamamı gerektiriyor. Ve 2. maddede ve farklı bir araçta (powermock) halihazırda uygulanmış olması gerçeği verildiğinde bir anlam ifade etmiyor.

Son olarak ... Özel yöntemlerle dalga geçmek, OO anlayışında bir sorun olduğuna dair bir ipucudur. OO'da yöntemlerin değil, nesnelerin (veya rollerin) işbirliği yapmasını istersiniz. Pascal ve prosedürel kodu unutun. Nesnelerde düşünün.


2
Bu ifadeyle yapılan ölümcül bir varsayım var:> Özel yöntemleri alay etmek, OO anlayışında bir sorun olduğuna dair bir ipucu. Herkese açık bir yöntemi test ediyorsam ve özel yöntemleri çağırırsa, özel yöntem dönüşleriyle alay etmek isterim. Yukarıdaki varsayıma göre gitmek, özel yöntemleri uygulama ihtiyacını bile ortadan kaldırır . OO hakkında nasıl kötü bir anlayış bu?
yumurtacıları

1
@eggmatters Baeldung'a göre "Alıştırma teknikleri sınıfın kendisine değil, sınıfın dış bağımlılıklarına uygulanmalıdır. Sınıflarımızı test etmek için özel yöntemlerle alay etmek gerekliyse, genellikle kötü bir tasarım olduğunu gösterir." İşte bununla ilgili harika bir konu softwareengineering.stackexchange.com/questions/100959/…
Jason Glez

35

İşte powermock ile nasıl yapılacağına dair küçük bir örnek

public class Hello {
    private Hello obj;
    private Integer method1(Long id) {
        return id + 10;
    }
} 

Yöntem 1'i test etmek için kodu kullanın:

Hello testObj = new Hello();
Integer result = Whitebox.invokeMethod(testObj, "method1", new Long(10L));

Özel nesne ayarlamak için obj bunu kullanın:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);

Bağlantınız yalnızca güç sahte deposunu gösteriyor @Mindaugas
Xavier

@Xavier doğru. Projenizde dilerseniz kullanabilirsiniz.
Mindaugas Jaraminas 04

1
Olağanüstü !!! çok iyi bu basit örnekte amaç sadece tüm çerçeve :) sağladıklarını kodu test etmek ve getirmemektir Çünkü hemen her şeyi :) ile açıklanabilir
siddhusingh

Lütfen bunu güncelleyin. Whitebox artık genel API'nin bir parçası değil.
user447607

17

Bunu hangi yöntemlerin olduğu ile değil, davranış açısından düşünün. Doğruysa çağrılan yöntemin methodbelirli bir davranışı vardır b. bYanlış ise farklı davranışı vardır . Bu, için iki farklı test yazmanız gerektiği anlamına gelir method; her durum için bir tane. Dolayısıyla, yönteme yönelik üç test yapmak yerine (biri için method, biri için method1, biri içinmethod2 , iki davranış odaklı testiniz var.

Bununla ilgili olarak (bunu yakın zamanda başka bir SO başlığında önerdim ve sonuç olarak dört harfli bir kelime olarak adlandırıldım, bu yüzden bunu bir tuz tanesi ile almaktan çekinmeyin); Yöntemin adı yerine test ettiğim davranışı yansıtan test adlarını seçmeyi faydalı buluyorum. Yani testleri demiyorlar testMethod(), testMethod1(), testMethod2()benzeri ve. Hangi davranışı test ettiğimi gösteren calculatedPriceIsBasePricePlusTax()veya gibi isimleri severim taxIsExcludedWhenExcludeIsTrue(); daha sonra her test yönteminde yalnızca belirtilen davranışı test edin. Bu tür davranışların çoğu, genel bir yönteme yalnızca bir çağrı içerecektir, ancak özel yöntemlere yapılan birçok çağrı içerebilir.

Bu yardımcı olur umarım.


15

Mockito bu özelliği sağlamazken, Mockito + JUnit ReflectionUtils sınıfını veya Spring ReflectionTestUtils sınıfını kullanarak aynı sonucu elde edebilirsiniz . Lütfen buradan özel bir yöntemin nasıl çağrılacağını açıklayan bir örneğe bakın :

ReflectionTestUtils.invokeMethod(student, "saveOrUpdate", "From Unit test");

ReflectionTestUtils ve Mockito ile eksiksiz örnekler, Mockito for Spring kitabında bulunabilir.


ReflectionTestUtils.invokeMethod (öğrenci, "saveOrUpdate", "bağımsız değişken1", "bağımsız değişken2", "bağımsız değişken3"); İnvokeMethod'un son argümanı, özel yönteme geçirilmesi gereken birden çok argüman alabilen Vargs'ı kullanır. işe yarıyor.
Tim

Özel yöntemleri test etmenin açık ara en kolay yolu olan bu yanıtın daha fazla olumlu oyu olmalıdır.
Maksimum

9

Özel yöntemleri test etmemelisiniz. Her halükarda özel yöntemleri çağırması gerektiğinden, yalnızca özel olmayan yöntemlerin test edilmesi gerekir. Özel yöntemleri test etmek "istiyorsanız", tasarımınızı yeniden düşünmeniz gerektiğini gösterebilir:

Uygun bağımlılık enjeksiyonu mu kullanıyorum? Özel yöntemleri ayrı bir sınıfa taşımam ve bunu test etmem gerekiyor mu? Bu yöntemler özel olmalı mı? ... varsayılan veya korumalı olamazlar mı?

Yukarıdaki örnekte, "rastgele" olarak adlandırılan iki yöntemin aslında kendi sınıflarına yerleştirilmesi, test edilmesi ve ardından yukarıdaki sınıfa enjekte edilmesi gerekebilir.


27
Geçerli bir nokta. Bununla birlikte, yöntemler için özel bir değiştirici kullanmanın nedeni, yalnızca çok uzun ve / veya tekrarlayan kodları kesmek istemeniz değil mi? Onu başka bir sınıf olarak ayırmak, bu kod satırlarını başka hiçbir yerde yeniden kullanılmayacak olan birinci sınıf vatandaşlar olarak tanıtıyorsunuz, çünkü özellikle uzun kodları bölümlemek ve kod satırlarının tekrarlanmasını önlemek için tasarlandı. Onu başka bir sınıfa ayıracaksanız, bu doğru gelmiyor; kolayca sınıf patlaması yaşarsınız.
supertonsky

2
Ünlü Supertonsky, ben genel durumdan bahsediyordum. Yukarıdaki durumda ayrı bir sınıfta olmaması gerektiğini kabul ediyorum. (Yorumunuzda +1 - özel üyelerin tanıtımını yapmak için çok geçerli bir nokta)
Jaco Van Niekerk

4
@supertonsky, bu soruna tatmin edici bir yanıt bulamadım. Özel üyeleri kullanmamın birkaç nedeni var ve çoğu zaman bir kod kokusu göstermiyorlar ve onları test etmekten büyük fayda sağlayacağım. İnsanlar "bunu yapma" diyerek bunu başından savıyor gibi görünüyor.
LuddyPants

3
Maalesef, "Özel yöntemleri test etmek 'istiyorsanız', tasarımınıza olumsuz oy vermeniz gerektiğini gösterebilir" temelinde olumsuz oy vermeyi seçtim. Tamam, yeterince adil, ancak test etmenin nedenlerinden biri, tasarımı yeniden düşünmek için zamanınız olmadığında, son teslim tarihine kadar, bir üzerinde yapılması gereken değişikliği güvenli bir şekilde uygulamaya çalışmanız özel yöntem. İdeal bir dünyada, tasarım mükemmel olacağı için özel yöntemin değiştirilmesi gerekmez mi? Elbette, ama mükemmel bir dünyada, ama tartışmalı çünkü testlere ihtiyaç duyan mükemmel bir dünyada, hepsi işe yarıyor. :)
John Lockwood

2
@John. Puan alındı, olumsuz oyunuz garanti edildi (+1). Yorumunuz için de teşekkürler - belirttiğiniz noktada size katılıyorum. Bu gibi durumlarda iki seçenekten birini görebiliyorum: Ya yöntem pakete özel ya da korumalı yapılır ve birim testleri her zamanki gibi yazılır; veya (ve bu yanak dili ve kötü bir uygulamadır), hala çalıştığından emin olmak için hızlı bir şekilde ana yöntem yazılır. Ancak, cevabım bir YENİ kodun yazıldığı ve orijinal tasarımda değişiklik yapamayacağınız zaman yeniden düzenleme yapılmadığı bir senaryoya dayanıyordu.
Jaco Van Niekerk

7
  1. Yansıma kullanılarak, özel yöntemler test sınıflarından çağrılabilir. Bu durumda,

    // test yöntemi şöyle olacak ...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        // invoke the private method for test
        privateMethod.invoke(A, null);
    
        }
    }
  2. Özel yöntem başka bir özel yöntemi çağırırsa, nesneyi gözetlememiz ve başka bir yöntemi saplamamız gerekir. Test sınıfı ...

    // test yöntemi şöyle olacak ...

    public class TestA {
    
      @Test
        public void testMethod() {
    
        A a= new A();
        A spyA = spy(a);
        Method privateMethod = A.class.getDeclaredMethod("method1", null);
        privateMethod.setAccessible(true);
        doReturn("Test").when(spyA, "method2"); // if private method2 is returning string data
        // invoke the private method for test
        privateMethod.invoke(spyA , null);
    
        }
    }

** Yaklaşım, yansıtma ve nesneyi gözetlemeyi birleştirmektir. ** yöntem1 ve ** yöntem2, özel yöntemler ve yöntem1 çağrıları yöntem2'dir.


6

Yansımayı kullanarak mockito kullanarak içeride özel bir yöntemi test edebildim. İşte örnek, mantıklı olacak şekilde adlandırmaya çalıştı

//Service containing the mock method is injected with mockObjects

@InjectMocks
private ServiceContainingPrivateMethod serviceContainingPrivateMethod;

//Using reflection to change accessibility of the private method

Class<?>[] params = new Class<?>[]{PrivateMethodParameterOne.class, PrivateMethodParameterTwo.class};
    Method m = serviceContainingPrivateMethod .getClass().getDeclaredMethod("privateMethod", params);
    //making private method accessible
    m.setAccessible(true); 
    assertNotNull(m.invoke(serviceContainingPrivateMethod, privateMethodParameterOne, privateMethodParameterTwo).equals(null));

4

Özel yöntemi test etme ihtiyacını gerçekten anlamıyorum. Temel sorun, genel yönteminizin dönüş türü olarak geçersiz olmasıdır ve bu nedenle genel yönteminizi test edemezsiniz. Bu nedenle özel yönteminizi test etmek zorunda kalıyorsunuz. Benim tahminim doğru mu?

Birkaç olası çözüm (AFAIK):

  1. Özel yöntemlerinizle dalga geçiyorsunuz, ancak yine de yöntemlerinizi "gerçekten" test etmeyeceksiniz.

  2. Yöntemde kullanılan nesnenin durumunu doğrulayın. ÇOĞUYLA yöntemler, giriş değerlerinin bazı işlemlerini gerçekleştirir ve bir çıktı döndürür veya nesnelerin durumunu değiştirir. Nesnelerin istenen durum için test edilmesi de kullanılabilir.

    public class A{
    
    SomeClass classObj = null;
    
    public void publicMethod(){
       privateMethod();
    }
    
    private void privateMethod(){
         classObj = new SomeClass();
    }
    
    }

    [Burada, classObj'nin null'dan null değil'e durum değişikliğini kontrol ederek özel yöntemi test edebilirsiniz.]

  3. Kodunuzu biraz yeniden düzenleyin (umarız bu eski bir kod değildir). Bir yöntem yazmanın temel mantığı, bir kişinin her zaman bir şeyi (a int / a boolean) döndürmesidir. Döndürülen değer, uygulama tarafından KULLANILMAMAKTADIR veya OLMAYABİLİR, ancak test tarafından KESİNLİKLE kullanılacaktır

    kodu.

    public class A
    { 
        public int method(boolean b)
        {
              int nReturn = 0;
              if (b == true)
                   nReturn = method1();
              else
                   nReturn = method2();
        }
    
        private int method1() {}
    
        private int method2() {}
    
    }

3

Mockito ile özel bir üyeden yöntemleri test etmenin bir yolu var. Diyelim ki böyle bir sınıfınız var:

public class A {
    private SomeOtherClass someOtherClass;
    A() {
        someOtherClass = new SomeOtherClass();
    }
    public void method(boolean b){
        if (b == true)
            someOtherClass.method1();
        else
            someOtherClass.method2();
    }

}

public class SomeOtherClass {
    public void method1() {}
    public void method2() {}
}

a.methodDenemek istiyorsanız bir yöntem çağıracaktır SomeOtherClass, aşağıdaki gibi bir şey yazabilirsiniz.

@Test
public void testPrivateMemberMethodCalled() {
    A a = new A();
    SomeOtherClass someOtherClass = Mockito.spy(new SomeOtherClass());
    ReflectionTestUtils.setField( a, "someOtherClass", someOtherClass);
    a.method( true );

    Mockito.verify( someOtherClass, Mockito.times( 1 ) ).method1();
}

ReflectionTestUtils.setField(); özel üyeyi casusluk yapabileceğiniz bir şeyle boğacaktır.


2

Testinizi aynı pakete, ancak farklı bir kaynak klasöre koyun (src / main / java vs. src / test / java) ve bu yöntemleri paket-özel hale getirin. Imo test edilebilirliği gizlilikten daha önemlidir.


6
Tek geçerli neden, eski bir sistemin bir bölümünü test etmektir. Özel / paket-özel yöntemi test etmeye başlarsanız, nesnenin dahili özelliklerini açığa çıkarırsınız. Bunu yapmak genellikle yeniden işlenemeyen kötü kodla sonuçlanır. Nesne yönelimli bir sistemin tüm iyiliği ile test edilebilirlik elde edebilmeniz için kompozisyonu tercih edin.
Brice

1
Kabul edildi - bu tercih edilen yol olacaktır. Bununla birlikte, özel yöntemleri gerçekten mockito ile test etmek istiyorsanız, sahip olduğunuz tek (tip güvenli) seçenek budur. Cevabım biraz aceleciydi, sizin ve diğerlerinin yaptığı gibi, risklere işaret etmeliydim.
Roland Schneider

Bu benim tercih ettiğim yol. Paket-özel düzeyinde dahili nesneyi ortaya çıkarmak yanlış bir şey değildir; ve birim testi beyaz kutu testidir, test için dahili testi bilmeniz gerekir.
Andrew Feng

0

Özel yöntemin geçersiz olmadığı ve dönüş değerinin harici bir bağımlılığın yöntemine bir parametre olarak kullanıldığı durumlarda, bağımlılıkla alay edebilir ArgumentCaptorve dönüş değerini yakalamak için bir kullanabilirsiniz . Örneğin:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");
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.