Тестирование частного метода с использованием 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. Из их вики

Почему Mockito не издевается над частными методами?

Во-первых, мы не догадываемся о насмешках над частными методами. Нас просто не волнуют частные методы, потому что с точки зрения тестирования частных методов не существует. Вот пара причин, по которым Mockito не издевается над частными методами:

Это требует взлома загрузчиков классов, который никогда не бывает пуленепробиваемым, и меняет api (вы должны использовать собственный тестовый бегун, аннотировать класс и т. Д.).

Обойти это очень просто - просто измените видимость метода с частного на защищенный пакетом (или защищенный).

Это требует, чтобы я тратил время на его внедрение и поддержку. И это не имеет смысла, учитывая пункт №2 и тот факт, что он уже реализован в другом инструменте (powermock).

Наконец ... Мокинг приватных методов - это намек на то, что что-то не так с объектно-ориентированным пониманием. В объектно-ориентированном стиле вы хотите, чтобы взаимодействовали объекты (или роли), а не методы. Забудьте о паскале и процедурном коде. Думайте объектами.

person Aravind Yarram    schedule 10.01.2012
comment
Это заявление делает фатальное предположение: ›Имитация частных методов - это намек на то, что что-то не так с объектно-ориентированным пониманием. Если я тестирую общедоступный метод и он вызывает частные методы, я бы хотел имитировать возвращаемые частные методы. Исходя из вышеизложенного предположения, отпадает необходимость даже в реализации частных методов. Как такое плохое понимание объектно-ориентированного программирования? - person eggmatters; 09.01.2020
comment
@eggmatters Согласно Баелдунгу методы имитации должны применяться к внешним зависимостям класса, а не к самому классу. Если имитация частных методов необходима для тестирования наших классов, это обычно указывает на плохой дизайн. Вот классная ветка об этом 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;
    }
} 

Чтобы проверить метод1, используйте код:

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 @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 for Spring

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 вызывает method2.

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
Отметил супертонский, я имел в виду общий случай. Я согласен с тем, что в приведенном выше случае это не должно быть в отдельном классе. (+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 / a boolean). Возвращаемое значение МОЖЕТ или НЕ МОЖЕТ использоваться реализацией, но ОБЯЗАТЕЛЬНО БУДЕТ использоваться тестом.

    код.

    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 vs. src / test / java) и сделайте эти методы пакетными. Тестируемость Imo важнее конфиденциальности.

person Roland Schneider    schedule 10.01.2012
comment
Единственная законная причина - протестировать часть унаследованной системы. Если вы начнете тестировать метод private / package-private, вы раскрываете внутреннюю структуру вашего объекта. Это обычно приводит к плохому рефакторингу кода. Используйте композицию PRefer, чтобы вы могли добиться тестируемости со всеми достоинствами объектно-ориентированной системы. - person Brice; 10.01.2012
comment
Согласен - это предпочтительный способ. Однако, если вы действительно хотите протестировать частные методы с помощью mockito, это единственный (типичный) вариант, который у вас есть. Мой ответ был немного поспешным, я должен был указать на риски, как вы и другие сделали это. - person Roland Schneider; 11.01.2012
comment
Это мой предпочтительный способ. Нет ничего плохого в том, чтобы предоставлять объект internal на уровне пакета; а модульный тест - это тестирование методом белого ящика, вам нужно знать внутреннее для тестирования. - 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