Тестирование частного метода с использованием 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 ответов:
невозможно через 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):
издеваясь над вашими частными методами, но все же вы не будете "на самом деле" тестировать свои методы.
проверьте состояние объект, используемый в методике. В основном методы либо выполняют некоторую обработку входных значений и возвращают выходные данные, либо изменяют состояние объектов. Проверка объектов на желаемое состояние также может быть использована.
public class A{ SomeClass classObj = null; public void publicMethod(){ privateMethod(); } private void privateMethod(){ classObj = new SomeClass(); } }
[здесь вы можете проверить частный метод, проверив изменение состояния classObj от null до not null.]
рефакторинг кода немного (надеюсь, что это не устаревший код). Мой Фунда писать метод заключается в том, что всегда нужно возвращать что-то (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 для весны