Тестирование приватного метода с использованием mockito - PullRequest
85 голосов
/ 10 января 2012
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 ???

Ответы [ 10 ]

118 голосов
/ 10 января 2012

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

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

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

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

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

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

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

71 голосов
/ 10 января 2012

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

28 голосов
/ 18 января 2013

Вот небольшой пример, как это сделать с 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);
15 голосов
/ 11 января 2012

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

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

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

6 голосов
/ 10 января 2012

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

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

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

5 голосов
/ 13 апреля 2016

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

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

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

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

2 голосов
/ 18 января 2012

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

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

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

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

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

    [Здесь вы можете проверить приватный метод, проверив изменение состояния 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() {}
    
    }
    
1 голос
/ 10 января 2012

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

0 голосов
/ 26 октября 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...