Тестирование приватного метода с использованием mockito

открытый класс A {

    метод public void (логическое b) {
          если (b == правда)
               метод1();
          еще
               метод2();
    }

    private void method1() {}
    private void method2() {}
}
открытый класс TestA {

    @Тестовое задание
    public void testMethod() {
      A a = mock(A.class);
      a.method(истина);
      // Как проверить как проверить (а).method1();
    }
}

Как проверить закрытый метод вызывается или нет, и как проверить закрытый метод с помощью mockito???

13 ответов

Решение

Вы не можете сделать это с Mockito, но вы можете использовать Powermock для расширения Mockito и имитации частных методов. Powermock поддерживает Mockito. Вот пример.

Не возможно через mockito. Из их вики

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

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

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

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

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

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

Вот небольшой пример, как это сделать с помощью 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);

Хотя Mockito не предоставляет такую ​​возможность, вы можете достичь того же результата, используя Mockito + класс JUnit ReflectionUtils или класс Spring ReflectionTestUtils. Пожалуйста, посмотрите нижеприведенный пример, объясняющий, как вызывать закрытый метод:

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

Полные примеры с ReflectionTestUtils и Mockito можно найти в книге Mockito for Spring

  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.

Думайте об этом с точки зрения поведения, а не с точки зрения методов, которые существуют. Метод называется method имеет особое поведение, если b правда. Это имеет другое поведение, если b ложно Это означает, что вы должны написать два разных теста для method; по одному на каждый случай. Таким образом, вместо трех ориентированных на метод тестов (один для methodодин для method1один для method2у вас есть два поведенческих теста.

В связи с этим (недавно я предложил это в другой ветке SO, и в результате меня назвали четырехбуквенным словом, так что не стесняйтесь воспринимать это с недоверием); Я считаю полезным выбирать имена тестов, которые отражают поведение, которое я тестирую, а не имя метода. Так что не называйте свои тесты testMethod(), testMethod1(), testMethod2() и так далее. Мне нравятся имена как calculatedPriceIsBasePricePlusTax() или же taxIsExcludedWhenExcludeIsTrue() которые показывают, какое поведение я тестирую; затем в каждом методе тестирования проверяйте только указанное поведение. В большинстве случаев такое поведение включает в себя только один вызов общедоступного метода, но может включать много обращений к частным методам.

Надеюсь это поможет.

Вы не должны проверять частные методы. Только непроверенные методы должны быть проверены, так как они все равно должны вызывать частные методы. Если вы "хотите" протестировать приватные методы, это может означать, что вам нужно переосмыслить свой дизайн:

Я использую правильную инъекцию зависимости? Возможно, мне нужно переместить приватные методы в отдельный класс и проверить это? Должны ли эти методы быть частными? ... они не могут быть по умолчанию или скорее защищены?

В приведенном выше примере два метода, которые вызываются "случайным образом", на самом деле, возможно, должны быть помещены в собственный класс, протестированы и затем введены в класс выше.

Я смог протестировать частный метод внутри, используя 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));

Я не очень понимаю вашу потребность в тестировании частного метода. Основная проблема заключается в том, что ваш публичный метод имеет void в качестве возвращаемого типа, и, следовательно, вы не можете протестировать свой публичный метод. Следовательно, вы вынуждены проверить свой частный метод. Правильно ли мое предположение??

Несколько возможных решений (AFAIK):

  1. Дразнить ваши личные методы, но вы все равно не будете "на самом деле" тестировать свои методы.

  2. Проверьте состояние объекта, используемого в методе. Методы MOSTLY либо выполняют некоторую обработку входных значений и возвращают выходные данные, либо изменяют состояние объектов. Тестирование объектов для желаемого состояния также может быть использовано.

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

    [Здесь вы можете проверить метод private, проверив изменение состояния classObj с нуля на ноль.]

  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() {}
    
    }
    

На самом деле есть способ протестировать методы от частного члена с помощью 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(); ошарашит частного участника чем-то, за кем вы можете шпионить.

Поместите ваш тест в тот же пакет, но в другую исходную папку (src/main/java и src/test/java) и сделайте эти методы закрытыми для пакета. Imo тестируемость важнее, чем конфиденциальность.

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

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

Основываясь на ответе @aravind-yarram: невозможно через mockito. Из их вики

Итак, каков объектно-ориентированный способ тестирования частных методов? Частные методы со сложной логикой могут быть признаком того, что ваш класс нарушает принцип единоличной ответственности и что часть логики следует перенести в новый класс.

Действительно, извлекая эти частные методы в общедоступные методы более детализированных классов, вы можете проводить их модульное тестирование, не нарушая инкапсуляцию исходного класса.

Другие вопросы по тегам