Тестване на личен метод с помощта на mockito

public class A {

    public void method(boolean b){
          if (b == true)
               method1();
          else
               method2();
    }

    private void method1() {}
    private void method2() {}
}
public class TestA {

    @Test
    public void testMethod() {
      A a = mock(A.class);
      a.method(true);
      //how to test like    verify(a).method1();
    }
}

Как да тествам частен метод дали се извиква или не и как да тествам частен метод с помощта на mockito???


person Nageswaran    schedule 10.01.2012    source източник


Отговори (12)


Не можете да направите това с Mockito, но можете да използвате Powermock, за да разширите Mockito и да имитирате частни методи. Powermock поддържа Mockito. Ето един пример.

person shift66    schedule 10.01.2012
comment
Объркан съм с този отговор. Това е подигравка, но заглавието тества частните методи - person diyoda_; 07.06.2017
comment
Използвах Powermock, за да се подигравам на частния метод, но как мога да тествам частния метод с Powermock. Къде мога да предам някакъв вход и да очаквам някакъв изход от метода и след това да проверя изхода? - person Rito; 27.10.2017
comment
Вие не можете. Подигравате се с входния изход, не можете да тествате реалната функционалност. - person Talha; 20.03.2018
comment
@diyoda_ Авторът иска да провери извикването на метода, че това трябва да се направи чрез подигравка. Можете да проверите подигравките само за съжаление. - person devaga; 12.01.2021

Не е възможно чрез mockito. От тяхното wiki

Защо Mockito не се подиграва с частни методи?

Първо, ние не сме догматични по отношение на подигравките с частни методи. Просто не ни интересуват частните методи, защото от гледна точка на тестването частни методи не съществуват. Ето няколко причини Mockito да не се подиграва на частните методи:

Изисква хакване на програми за зареждане на класове, което никога не е бронирано и променя API (трябва да използвате персонализиран тестов инструмент, да анотирате класа и т.н.).

Много е лесно да се заобиколи - просто променете видимостта на метода от частен на защитен от пакет (или защитен).

Това изисква от мен да отделям време за внедряването и поддържането му. И няма смисъл предвид точка #2 и факта, че вече е имплементирана в друг инструмент (powermock).

И накрая... Подигравката с частните методи е намек, че има нещо нередно с разбирането на OO. В OO искате обекти (или роли) да си сътрудничат, а не методи. Забравете за паскала и процедурния код. Мислете в обекти.

person Aravind Yarram    schedule 10.01.2012
comment
Има фатално предположение, направено от това твърдение: ›Подиграването на частните методи е намек, че има нещо нередно с разбирането на OO. Ако тествам публичен метод и той извиква частни методи, бих искал да се подигравам на връщанията на частния метод. Придържайки се към горното предположение, се избягва необходимостта дори от имплементиране на частни методи. Как е това лошо разбиране на OO? - person eggmatters; 09.01.2020
comment
@eggmatters Според Baeldung Подигравателните техники трябва да се прилагат към външните зависимости на класа, а не към самия клас. Ако подигравката на частните методи е от съществено значение за тестване на нашите класове, това обикновено показва лош дизайн. Ето страхотна тема за това softwareengineering.stackexchange.com/questions/100959/ - person Jason Glez; 08.02.2020
comment
ако виждате обект като нещо, което трябва да тествате, може да е правилно. но ако искате да тествате функционалността, тогава функциите са нещото, към което искате да отидете. функциите са за модулиране и наличието на модул би означавало първо да го проверите до разумна степен, преди да проверите нещо, което го използва - това улеснява и подобрява цялостното качество на резултатите от тестването. - person Alexander Stohr; 16.04.2021

Ето малък пример как да го направите с powermock

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

За да тествате method1 използвайте код:

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

За да зададете частен обект obj използвайте това:

Hello testObj = new Hello();
Hello newObject = new Hello();
Whitebox.setInternalState(testObj, "obj", newObject);
person Mindaugas Jaraminas    schedule 18.01.2013
comment
Вашата връзка сочи само към power mock repo @Mindaugas - person Xavier; 02.09.2017
comment
@Xavier вярно. Можете да го използвате, ако желаете във вашия проект. - person Mindaugas Jaraminas; 04.09.2017
comment
Чудесен !!! така добре обяснено с тези прости примери почти всичко :) Защото целта е просто да се тества кода, а не това, което рамката предоставя :) - person siddhusingh; 10.09.2017
comment
Моля, актуализирайте това. Whitebox вече не е част от публичния API. - person user447607; 01.02.2018

Докато Mockito не предоставя тази възможност, можете да постигнете същия резултат, като използвате Mockito + класа JUnit ReflectionUtils или Spring ReflectionTestUtils клас. Моля, вижте пример по-долу, взет от тук, обясняващ как за извикване на частен метод:

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

Пълни примери с ReflectionTestUtils и Mockito могат да бъдат намерени в книгата Mockito за пролетта

person AR1    schedule 22.09.2018
comment
ReflectionTestUtils.invokeMethod(студент, saveOrUpdate, аргумент1, аргумент2, аргумент3); Последният аргумент на invokeMethod използва Vargs, които могат да приемат множество аргументи, които трябва да бъдат предадени на частния метод. работи. - person Tim; 13.08.2019
comment
Този отговор трябва да има повече положителни гласове, далеч най-лесният начин за тестване на частни методи. - person maxeh; 08.09.2019

Мислете за това от гледна точка на поведение, а не от гледна точка на това какви методи има. Методът, наречен method, има определено поведение, ако b е вярно. Има различно поведение, ако b е невярно. Това означава, че трябва да напишете два различни теста за method; по един за всеки случай. Така че вместо да имате три теста, ориентирани към метода (един за method, един за method1, един за method2, имате два теста, ориентирани към поведението.

Свързано с това (предложих това в друга тема на SO наскоро и в резултат на това ме нарекоха дума от четири букви, така че не се колебайте да приемете това със зърно сол); Намирам за полезно да избирам имена на тестове, които отразяват поведението, което тествам, а не името на метода. Така че не наричайте тестовете си testMethod(), testMethod1(), testMethod2() и така нататък. Харесвам имена като calculatedPriceIsBasePricePlusTax() или taxIsExcludedWhenExcludeIsTrue(), които показват какво поведение тествам; след това в рамките на всеки тестов метод тествайте само посоченото поведение. Повечето такива поведения ще включват само едно извикване на публичен метод, но могат да включват много извиквания на частни методи.

Надявам се това да помогне.

person Dawood ibn Kareem    schedule 10.01.2012

  1. Чрез използване на отражение частните методи могат да бъдат извикани от тестови класове. В такъв случай,

    // методът на тестване ще бъде като този ...

    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. Ако частният метод извиква друг частен метод, тогава трябва да шпионираме обекта и да заглушим друг метод. Тестовият клас ще бъде като ...

    // методът на тестване ще бъде като този ...

    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);
    
        }
    }
    

**Подходът е да се комбинира отражение и шпиониране на обекта. **method1 и **method2 са частни методи и method1 извиква метод2.

person Singh Arun    schedule 18.01.2020
comment
Гореспоменатите работят перфектно. Благодаря ти - person Manish; 11.03.2021

Не трябва да тествате частни методи. Трябва да се тестват само нечастни методи, тъй като те така или иначе трябва да извикват частните методи. Ако „искате“ да тествате частни методи, това може да означава, че трябва да преосмислите своя дизайн:

Използвам ли правилно инжектиране на зависимост? Възможно ли е да преместя частните методи в отделен клас и по-скоро да го тествам? Трябва ли тези методи да са лични? ... не могат ли да бъдат по подразбиране или по-скоро защитени?

В горния пример, двата метода, които се наричат ​​„случайно“, може действително да трябва да бъдат поставени в собствен клас, тествани и след това инжектирани в класа по-горе.

person Jaco Van Niekerk    schedule 10.01.2012
comment
Валидна точка. Но не е ли причината да използвате частен модификатор за методи, защото просто искате да отрежете кодове, които са твърде дълги и/или повтарящи се? Отделянето му като друг клас е все едно да популяризирате тези редове кодове като първокласни граждани, които няма да бъдат използвани повторно никъде другаде, защото е предназначен специално за разделяне на дълги кодове и за предотвратяване на повтарящи се редове кодове. Ако ще го отделите в друг клас, просто не се чувства добре; лесно ще получите класова експлозия. - person supertonsky; 27.02.2013
comment
Отбелязан supertonsky, имах предвид общия случай. Съгласен съм, че в горния случай не трябва да е в отделен клас. (Въпреки това +1 за коментара ви - това е много валидно мнение, което правите относно насърчаването на частни членове) - person Jaco Van Niekerk; 28.02.2013
comment
@supertonsky, не успях да намеря задоволителен отговор на този проблем. Има няколко причини, поради които мога да използвам частни членове и много често те не показват миризма на код и ще имам голяма полза от тестването им. Хората изглежда отхвърлят това, като казват просто не го правете. - person LuddyPants; 27.01.2014
comment
Съжалявам, избрах да гласувам против въз основа на Ако „искате“ да тествате частни методи, това може да означава, че трябва да гласувате против своя дизайн. Добре, честно, но една от причините изобщо да тествате е, че в краен срок, когато нямате време да преосмислите дизайна, се опитвате безопасно да приложите промяна, която трябва да бъде направена в частен метод. В един идеален свят няма ли нужда този частен метод да се променя, защото дизайнът би бил перфектен? Разбира се, но в един перфектен свят, но това е спорно, защото в един перфектен свят, който има нужда от тестове, всичко просто работи. :) - person John Lockwood; 12.03.2014
comment
@Джон. Точката взета, вашият глас против е оправдан (+1). Благодаря и за коментара - съгласен съм с теб в това, което правиш. В такива случаи мога да видя една от двете опции: или методът да бъде направен частен за пакета или защитен и тестовете за единици са написани както обикновено; или (и това е насмешка и лоша практика) бързо се пише главен метод, за да се уверите, че все още работи. Моят отговор обаче се основаваше на сценарий за писане на НОВ код и без рефакторинг, когато може да не успеете да промените оригиналния дизайн. - person Jaco Van Niekerk; 13.03.2014
comment
Преместването на методи в техния собствен клас или превръщането им в обществено достояние, само за да се активира функционалност в пакет за тестване, определено е лоша практика. И не трябва да тествате частни методи. Категорично не съм съгласен. Частните методи имат своя собствена сложност и следователно са напълно разумни за тестване. - person Luke; 19.08.2019
comment
Това. Частният метод не е част от договор и следователно не трябва да се отнася до модулен тест. Единично тестване на частни методи или с други думи писането на частни метод зависими единични тестове е лоша практика. - person NotGaeL; 30.09.2020

Успях да тествам частен метод вътре, използвайки mockito, използвайки отражение. Ето примера, опитах се да го нарека така, че да има смисъл

//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));
person Abdullah Choudhury    schedule 13.04.2016

Наистина не разбирам нуждата ви да тествате частния метод. Основният проблем е, че вашият публичен метод има void като тип на връщане и следователно не можете да тествате своя публичен метод. Следователно вие сте принудени да тествате личния си метод. Правилно ли е предположението ми??

Няколко възможни решения (AFAIK):

  1. Подигравайки се с личните си методи, но все пак няма да "действително" тествате методите си.

  2. #P4#
    public class A{
    
    SomeClass classObj = null;
    
    public void publicMethod(){
       privateMethod();
    }
    
    private void privateMethod(){
         classObj = new SomeClass();
    }
    
    }
    
    #P5#
  3. Преработете малко кода си (Надявам се, че това не е наследен код). Моята основа за писане на метод е, че човек винаги трябва да връща нещо (int/булев). Върнатата стойност МОЖЕ или НЕ МОЖЕ да се използва от изпълнението, но СИГУРНО ще БЪДЕ използвана от теста

    код.

    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() {}
    
    }
    
person Reji    schedule 17.01.2012

Всъщност има начин да тествате методи от частен член с Mockito. Да приемем, че имате клас като този:

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.method ще извика метод от SomeOtherClass, можете да напишете нещо подобно по-долу.

@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(); ще заличи личния член с нещо, което можете да шпионирате.

person Fan Jin    schedule 26.10.2018

Поставете теста си в същия пакет, но различна папка източник (src/main/java срещу src/test/java) и направете тези методи пакетни частни. Възможността за тестване на Imo е по-важна от поверителността.

person Roland Schneider    schedule 10.01.2012
comment
Единствената законна причина е да се тества част от наследена система. Ако започнете да тествате метод private/package-private, вие излагате вътрешните елементи на вашия обект. Това обикновено води до лош преработваем код. Предпочитайте композиция, за да можете да постигнете възможност за тестване с всички предимства на обектно-ориентирана система. - person Brice; 10.01.2012
comment
Съгласен - това би бил предпочитаният начин. Въпреки това, ако наистина искате да тествате частни методи с mockito, това е единствената (безопасна за тип) опция, която имате. Отговорът ми обаче беше малко прибързан, трябваше да посоча рисковете, както ти и другите го направихте. - person Roland Schneider; 11.01.2012
comment
Това е моят предпочитан начин. Няма нищо лошо в излагането на вътрешния обект на частно ниво на пакета; и единичният тест е тестване в бяла кутия, трябва да знаете вътрешния за тестване. - person Andrew Feng; 25.01.2017

В случаите, когато частният метод не е невалиден и върнатата стойност се използва като параметър към метода на външна зависимост, можете да се подиграете на зависимостта и да използвате ArgumentCaptor, за да уловите върнатата стойност. Например:

ArgumentCaptor<ByteArrayOutputStream> csvOutputCaptor = ArgumentCaptor.forClass(ByteArrayOutputStream.class);
//Do your thing..
verify(this.awsService).uploadFile(csvOutputCaptor.capture());
....
assertEquals(csvOutputCaptor.getValue().toString(), "blabla");
person milensky    schedule 06.08.2020