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

9 69

9 ответов:

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

невозможно через mockito. От их wiki

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

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

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

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

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

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

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

подумайте об этом с точки зрения поведения, а не с точки зрения того, какие методы существуют. Метод называется 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. проверьте состояние объект, используемый в методике. В основном методы либо выполняют некоторую обработку входных значений и возвращают выходные данные, либо изменяют состояние объектов. Проверка объектов на желаемое состояние также может быть использована.

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

    [здесь вы можете проверить частный метод, проверив изменение состояния classObj от null до not null.]

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

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

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

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

полные примеры с ReflectionTestUtils и Mockito можно найти в книге Mockito для весны