Как unit test (используя xUnit) класс, который имеет внутренние частные методы, поля или вложенные классы? Или функция, которая сделана частной с помощью внутренней связи (static
в C/С++) или находится в частной (anonymous) пространство имен?
Кажется, плохо изменить модификатор доступа для метода или функции, чтобы иметь возможность запускать тест.
Если у вас есть какое-то устаревшее Java- приложение, и вам не разрешено изменять видимость ваших методов, лучший способ протестировать частные методы - это использовать отражение.
Внутренне мы используем помощники для получения/установки private
и private static
переменных, а также для вызова private
и private static
методов. Следующие шаблоны позволят вам делать практически все, что связано с закрытыми методами и полями. Конечно, вы не можете изменить private static final
переменные с помощью отражения.
Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);
И для полей:
Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
Заметки:
1.TargetClass.getDeclaredMethod(methodName, argClasses)
позволяет вам изучитьprivate
методы. То же самое относится и кgetDeclaredField
.
2.setAccessible(true)
требуется для игры с рядовыми.
Лучшим способом тестирования частного метода является использование другого общедоступного метода. Если это невозможно сделать, то выполняется одно из следующих условий:
Когда у меня есть частные методы в достаточно сложном классе, я чувствую необходимость непосредственного тестирования частных методов, это запах кода: мой класс слишком сложный.
Мой обычный подход к решению таких проблем - вызывать новый класс, содержащий интересные биты. Часто этот метод и поля, с которыми он взаимодействует, и, возможно, другой метод или два могут быть извлечены в новый класс.
Новый класс предоставляет эти методы как "общедоступные", поэтому они доступны для модульного тестирования. Новые и старые классы теперь и проще, чем оригинальный класс, что отлично для меня (мне нужно, чтобы все было просто, или я заблудился!).
Обратите внимание, что я не предлагаю, чтобы люди создавали классы, не используя свой мозг! Здесь нужно использовать силы модульного тестирования, чтобы помочь вам найти хорошие новые классы.
Я использовал reflection, чтобы сделать это для Java в прошлом, и, на мой взгляд, это была большая ошибка.
Строго говоря, вы не должны писать модульные тесты, которые непосредственно проверяют частные методы. То, что вы должны тестировать, - это публичный контракт, который класс имеет с другими объектами; вы никогда не должны напрямую тестировать внутренние объекты. Если другой разработчик хочет внести небольшое изменение в класс, что не влияет на публичный контракт классов, он должен изменить свой тест на основе отражения, чтобы убедиться, что он работает. Если вы делаете это неоднократно по всему проекту, модульные тесты затем перестают быть полезным измерением здоровья кода и начинают становиться препятствием для развития и раздражением команды разработчиков.
Вместо этого я рекомендую использовать инструмент для покрытия кода, такой как Cobertura, чтобы убедиться, что тесты модуля, которые вы пишете, обеспечивают достойный охват кода частными методами. Таким образом, вы косвенно проверяете, что делают частные методы, и сохраняют более высокий уровень ловкости.
Из этой статьи: Тестирование частных методов с помощью JUnit и SuiteRunner (Bill Venners), у вас в основном есть 4 варианта:
- Не проверяйте частные методы.
- Предоставьте доступ к пакетам методов.
- Использовать вложенный тестовый класс.
- Используйте отражение.
Обычно unit test предназначен для реализации открытого интерфейса класса или единицы. Таким образом, частные методы - это детализация реализации, которые вы не ожидаете явно проверить.
Только два примера того, где я хочу протестировать частный метод:
SecurityManager
не настроен для предотвращения этого).Я понимаю идею только тестирования "контракта". Но я не вижу, чтобы кто-то мог защищать на самом деле не тестирование кода - ваш пробег может меняться.
Таким образом, мой компромисс включает в себя усложнение JUnits с отражением, а не компрометацию моей безопасности и SDK.
private
не единственная альтернатива Никакой модификатор доступа не является package private
и означает, что вы можете выполнить его модульное тестирование, пока ваш модульный тест находится в одном пакете.
Частные методы вызывается общедоступным методом, поэтому входы в ваши общедоступные методы также должны проверять частные методы, вызываемые этими общедоступными методами. Когда открытый метод выходит из строя, это может быть сбой в частном методе.
Другим подходом, который я использовал, является изменение частного метода для частного или защищенного пакета, а затем его дополнение с помощью аннотации @VisibleForTesting библиотеки Google Guava.
Это сообщит кому-либо, использующему этот метод, проявлять осторожность и не обращаться к нему напрямую даже в пакете. Кроме того, тестовый класс не должен находиться в одном пакете физически, но в том же пакете в папке test.
Например, если тестируемый метод находится в src/main/java/mypackage/MyClass.java
, тогда ваш тестовый вызов должен быть помещен в src/test/java/mypackage/MyClassTest.java
. Таким образом, вы получили доступ к методу тестирования в своем тестовом классе.
Чтобы протестировать устаревший код с большими и причудливыми классами, часто очень полезно иметь возможность тестировать один частный (или общедоступный) метод, который я пишу прямо сейчас.
Я использую junitx.util.PrivateAccessor -пакет для Java. Много полезных однострочных линий для доступа к частным методам и частным полям.
import junitx.util.PrivateAccessor;
PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);
Class
качестве первого параметра в этих методах, вы получаете доступ только к static
элементам.
Попробовав решение Cem Catikkas используя отражение для Java, я бы сказал, что его было более элегантным решением, чем я описал здесь. Однако, если вы ищете альтернативу использованию отражения и имеете доступ к источнику, который вы тестируете, это все равно будет вариантом.
Существует возможность заслужить тестирование частных методов класса, в частности, тестовая разработка, где вы хотели бы разработать небольшие тесты прежде чем писать код.
Создание теста с доступом к частным членам и методам может тестировать области кода, которые трудно поддаются таргетингу только с доступом только к общедоступным методам. Если публичный метод имеет несколько шагов, он может состоять из нескольких частных методов, которые затем могут быть протестированы индивидуально.
Преимущества:
Недостатки:
Однако, если для непрерывного тестирования требуется этот метод, это может быть сигнал о том, что частные методы должны быть извлечены, которые могут быть протестированы традиционным, общедоступным способом.
Вот скрупулезный пример того, как это будет работать:
// Import statements and package declarations
public class ClassToTest
{
private int decrement(int toDecrement) {
toDecrement--;
return toDecrement;
}
// Constructor and the rest of the class
public static class StaticInnerTest extends TestCase
{
public StaticInnerTest(){
super();
}
public void testDecrement(){
int number = 10;
ClassToTest toTest= new ClassToTest();
int decremented = toTest.decrement(number);
assertEquals(9, decremented);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(StaticInnerTest.class);
}
}
}
Внутренний класс будет скомпилирован в ClassToTest$StaticInnerTest
.
Смотрите также: Совет Java 106: Статические внутренние классы для удовольствия и прибыли
Как говорили другие... не проверяйте частные методы напрямую. Вот несколько мыслей:
Запустите покрытие кода на модульных тестах. Если вы видите, что методы не полностью протестированы, добавьте тесты для получения покрытия. Стремитесь к 100% охвату кода, но поймите, что вы, вероятно, не получите его.
Если использовать Spring, ReflectionTestUtils предоставляет некоторые удобные инструменты, которые помогают здесь с минимальными усилиями. Например, чтобы настроить макет на частном члене, не будучи вынужденным добавить нежелательный публичный сеттер:
ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);
Частные методы используются общественными. В противном случае они являются мертвым кодом. Вот почему вы проверяете публичный метод, утверждаете ожидаемые результаты публичного метода и тем самым частные методы, которые он потребляет.
Тестирование частных методов должно быть проверено путем отладки перед запуском ваших модульных тестов в общедоступных методах.
Они также могут быть отлажены с помощью тестовой разработки, отладки ваших модульных тестов до тех пор, пока не будут выполнены все ваши утверждения.
Я лично считаю, что лучше создавать классы с использованием TDD; создавая публичные куски методов, затем генерируя единичные тесты с помощью all утверждений, определенных заранее, поэтому ожидаемый результат метода определяется до того, как вы его закодируете. Таким образом, вы не ошибетесь, чтобы утверждения unit test соответствовали результатам. Затем ваш класс прочен и соответствует требованиям, когда проходят все тесты вашего устройства.
В Spring Framework вы можете протестировать закрытые методы, используя этот метод:
ReflectionTestUtils.invokeMethod()
Например:
ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");
Если вы пытаетесь протестировать существующий код, который вы неохотно или не можете изменить, отражение является хорошим выбором.
Если дизайн класса по-прежнему гибкий, и у вас есть сложный частный метод, который вы хотите протестировать отдельно, я предлагаю вам вытащить его в отдельный класс и протестировать этот класс отдельно. Это не должно изменять открытый интерфейс исходного класса; он может внутренне создать экземпляр класса-помощника и вызвать вспомогательный метод.
Если вы хотите проверить сложные условия ошибки, исходящие из вспомогательного метода, вы можете пойти дальше. Извлеките интерфейс из вспомогательного класса, добавьте публичный getter и setter в исходный класс, чтобы ввести класс-помощник (используемый через его интерфейс), а затем введите макетную версию вспомогательного класса в исходный класс, чтобы проверить, как исходный класс отвечает на исключения из помощника. Этот подход также полезен, если вы хотите протестировать исходный класс, не тестируя также вспомогательный класс.
Тестирование частных методов ломает инкапсуляцию вашего класса, потому что каждый раз, когда вы меняете внутреннюю реализацию, вы нарушаете клиентский код (в данном случае, тесты).
Поэтому не проверяйте частные методы.
Ответ от Страница часто задаваемых вопросов JUnit.org:
Но если вам нужно...
Если вы используете JDK 1.3 или выше, вы можете использовать отражение для подрыва механизм управления доступом с помощью PrivilegedAccessor. Подробнее о том, как его использовать, читайте в этой статье.
Если вы используете JDK 1.6 или выше, и вы комментируете свои тесты с помощью @Test, вы можете использовать Dp4j для отражения в ваших методах тестирования. Для подробности о том, как его использовать, см. этот тест script.
P.S. Я главный вклад в Dp4j, спросите меня, если вам нужно Помогите.:)
Если вы хотите протестировать частные методы устаревшего приложения, в котором вы не можете изменить код, один из вариантов для Java - jMockit, который позволит вам создавать mocks для объекта, даже если они являются закрытыми для класса.
Deencapsulation.invoke(instance, "privateMethod", param1, param2);
Если вы используете JUnit, посмотрите junit-addons. Он имеет возможность игнорировать модель безопасности Java и получать доступ к приватным методам и атрибутам.
Я стараюсь не тестировать частные методы. Там царит безумие. Лично я считаю, что вы должны тестировать только открытые интерфейсы (и которые включают защищенные и внутренние методы).
Я бы посоветовал вам немного переработать ваш код. Когда вы должны начать думать об использовании отражений или других вещей, просто проверяя свой код, что-то не так с вашим кодом.
Вы упомянули о разных типах проблем. Давайте начнем с частных полей. В случае частных полей я бы добавил новый конструктор и ввел в него поля. Вместо этого:
public class ClassToTest {
private final String first = "first";
private final List<String> second = new ArrayList<>();
...
}
Я бы использовал это:
public class ClassToTest {
private final String first;
private final List<String> second;
public ClassToTest() {
this("first", new ArrayList<>());
}
public ClassToTest(final String first, final List<String> second) {
this.first = first;
this.second = second;
}
...
}
Это не будет проблемой даже с некоторым устаревшим кодом. Старый код будет использовать пустой конструктор, и если вы спросите меня, реорганизованный код будет выглядеть более чистым, и вы сможете вводить необходимые значения в тест без отражения.
Теперь о частных методах. В моем личном опыте, когда вы должны заглушить частный метод тестирования, этот метод не имеет ничего общего с этим классом. В этом случае общим шаблоном было бы обернуть его в интерфейсе, например Callable
, а затем передать этот интерфейс также в конструкторе (с помощью этого трюка с несколькими конструкторами):
public ClassToTest() {
this(...);
}
public ClassToTest(final Callable<T> privateMethodLogic) {
this.privateMethodLogic = privateMethodLogic;
}
В основном все, что я написал, выглядит как шаблон инъекции зависимостей. В моем личном опыте это действительно полезно при тестировании, и я думаю, что этот вид кода чище и будет легче поддерживать. Я бы сказал то же самое о вложенных классах. Если вложенный класс содержит тяжелую логику, было бы лучше, если бы вы перенесли его в качестве частного класса пакета и ввели его в класс, требующий его.
Есть также несколько других шаблонов проектирования, которые я использовал при рефакторинге и сохранении устаревшего кода, но все зависит от того, какие тесты проверяют ваш код. Использование отражения в основном не является проблемой, но когда у вас есть корпоративное приложение, которое сильно тестируется и тесты запускаются до каждого развертывания, все становится очень медленным (это просто раздражает, и мне не нравятся такие вещи).
Существует также инъекция установки, но я бы не рекомендовал ее использовать. Лучше придерживаться конструктора и инициализировать все, когда это действительно необходимо, оставляя возможность для инъекции необходимых зависимостей.
ClassToTest(Callable)
. Это делает ClassToTest
более сложным. Будь проще. Кроме того, для этого требуется, чтобы кто-то еще сказал ClassToTest
что-то, ClassToTest
должен руководить ClassToTest
. Есть место для введения логики, но это не так. Вы только что сделали класс труднее поддерживать, а не легче.
X
не увеличивает сложность X
до такой степени, что это может вызвать больше проблем ... и, следовательно, требует дополнительного тестирования ... что, если вы реализовали таким образом, что может вызвать больше проблем ... (это не бесконечные циклы; каждая итерация, вероятно, меньше, чем предыдущая, но все же раздражает)
Частный метод доступен только для одного класса. Таким образом, нет способа протестировать метод "private" целевого класса из любого тестового класса. Выход состоит в том, что вы можете выполнить модульное тестирование вручную или можете изменить свой метод от "private" до "protected".
И тогда защищенный метод может быть доступен только в том же пакете, где определен класс. Таким образом, тестирование защищенного метода целевого класса означает, что нам нужно определить ваш тестовый класс в том же пакете, что и целевой класс.
Если все вышеизложенное не соответствует вашему требованию, используйте способ отражения для доступа к приватному методу.
Вот моя общая функция для проверки частных полей:
protected <F> F getPrivateField(String fieldName, Object obj)
throws NoSuchFieldException, IllegalAccessException {
Field field =
obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return (F)field.get(obj);
}
Сегодня я нажал библиотеку Java, чтобы помочь тестировать частные методы и поля. Он был разработан с учетом Android, но он действительно может быть использован для любого проекта Java.
Если у вас есть код с частными методами или полями или конструкторами, вы можете использовать BoundBox. Он делает именно то, что вы ищете. Ниже приведен пример теста, который обращается к двум частным полям активности Android для его проверки:
@UiThreadTest
public void testCompute() {
// Given
boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());
// When
boundBoxOfMainActivity.boundBox_getButtonMain().performClick();
// Then
assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}
BoundBox позволяет легко тестировать частные/защищенные поля, методы и конструкторы. Вы даже можете получить доступ к материалам, скрытым по наследству. Действительно, BoundBox нарушает инкапсуляцию. Это даст вам доступ ко всему, что через отражение, НО все проверяется во время компиляции.
Он идеально подходит для тестирования некоторого устаревшего кода. Используйте его осторожно.;)
Как было сказано выше, хороший способ - проверить их через ваши общедоступные интерфейсы.
Если вы это сделаете, рекомендуется использовать инструмент покрытия кода (например, Эмма), чтобы проверить, действительно ли ваши частные методы выполняются из ваших тестов.
См. ниже пример:
Необходимо добавить следующий оператор импорта:
import org.powermock.reflect.Whitebox;
Теперь вы можете напрямую передать объект, у которого есть частный метод, имя метода, которое будет вызываться, и дополнительные параметры, как показано ниже.
Whitebox.invokeMethod(obj, "privateMethod", "param1");
Недавно я столкнулся с этой проблемой и написал небольшой инструмент под названием Picklock, который позволяет избежать проблем с явным использованием API отражения Java, два примера:
Методы вызова, например. private void method(String s)
- отражением Java
Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");
Методы вызова, например. private void method(String s)
- by Picklock
interface Accessible {
void method(String s);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");
Настройка полей, например. private BigInteger amount;
- отражением Java
Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));
Настройка полей, например. private BigInteger amount;
- by Picklock
interface Accessible {
void setAmount(BigInteger amount);
}
...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));
Во-первых, я вышлю этот вопрос: почему ваши частные члены нуждаются в изолированном тестировании? Являются ли они такими сложными, обеспечивая такое сложное поведение, чтобы требовать тестирования отдельно от публичной поверхности? Это модульное тестирование, а не тестирование "линии-кода". Не потейте мелкие вещи.
Если они такие большие, достаточно большие, чтобы эти частные члены были "сложной" единицей, рассмотрите возможность реорганизации таких частных членов из этого класса.
Если рефакторинг является неприемлемым или неосуществимым, можете ли вы использовать шаблон стратегии для замены доступа к этим частным функциям-членам/классам участников в unit test? В unit test стратегия обеспечит дополнительную валидацию, но в релиз-сборках это будет простой переход.
PowerMockito создан для этого. Использовать зависимость maven
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-mockito-release-full</artifactId>
<version>1.6.4</version>
</dependency>
Затем вы можете сделать
import org.powermock.reflect.Whitebox;
...
MyClass sut = new MyClass();
SomeType rval = Whitebox.invokeMethod(sut, "myPrivateMethod", params, moreParams);
Для Java я бы использовал отражение, так как мне не нравится идея изменить доступ к пакету по объявленному методу просто для тестирования. Тем не менее, я обычно просто проверяю общедоступные методы, которые также должны гарантировать, что частные методы работают правильно.
вы не можете использовать отражение для получения частных методов вне класса владельца, частный модификатор влияет на отражение также
Это неверно. Вы, безусловно, можете, как упоминалось в ответить Cem Catikkas.
System.Reflection
». (Очевидно, вам нужен ReflectionPermission
, но обычно это не проблема.)
В С++: перед включением заголовка класса, который имеет частную функцию, которую вы хотите проверить,
Используйте этот код:
#define private public
#define protected public
Быстрое добавление комментария @Cem Catikka при использовании ExpectedException:
Имейте в виду, что ваше ожидаемое исключение будет обернуто в InvocationTargetException, поэтому, чтобы добраться до вашего исключения, вам придется бросить причину InvocationTargetException, которую вы получили. Что-то вроде (проверка частного метода validateRequest() в BizService):
@Rule
public ExpectedException thrown = ExpectedException.none();
@Autowired(required = true)
private BizService svc;
@Test
public void testValidateRequest() throws Exception {
thrown.expect(BizException.class);
thrown.expectMessage(expectMessage);
BizRequest request = /* Mock it, read from source - file, etc. */;
validateRequest(request);
}
private void validateRequest(BizRequest request) throws Exception {
Method method = svc.getClass().getDeclaredMethod("validateRequest", BizRequest.class);
method.setAccessible(true);
try {
method.invoke(svc, request);
}
catch (InvocationTargetException e) {
throw ((BizException)e.getCause());
}
}
Я не уверен, является ли это хорошей техникой, но я разработал следующий шаблон для unit test частных методов:
Я не изменяю видимость метода, который я хочу проверить, и добавляю дополнительный метод. Вместо этого я добавляю дополнительный публичный метод для каждого частного метода, который я хочу проверить. Я вызываю этот дополнительный метод Test-Port
и обозначаю их префиксом t_
. Этот метод Test-Port
затем просто обращается к соответствующему частному методу.
Кроме того, я добавляю флаг boolean в метод Test-Port
, чтобы решить, предоставляю ли я доступ к приватному методу с помощью метода Test-Port
извне. Этот флаг затем устанавливается глобально в статическом классе, где я размещаю, например. другие глобальные настройки для приложения. Поэтому я могу включить или отключить доступ к частным методам в одном месте, например. в соответствующем unit test.
Вы можете отключить ограничения доступа Java для отражения, чтобы частные ничего не делали.
Вызов setAccessible(true)
делает это.
Единственное ограничение заключается в том, что ClassLoader может запретить вам это делать.
См. Подчинение защиты доступа Java для модульного тестирования (Росс Бертон) для способа сделать это на Java.
Лучшим и правильным юридическим способом тестирования частного Java-метода из тестовой среды является аннотация @VisibleForTesting по методу, поэтому тот же метод будет отображаться для тестовой среды как обычный публичный метод.
Я хотел бы поделиться с вами тем, что вы обеспокоены тем, что не тестируете частные методы, как предлагают многие посты, и учтите, что инструмент покрытия кода точно определит, сколько кода тестируется и где он утекает, так что это количественно приемлемо для сделай это.
Я собираюсь сказать то, что нужно сказать, но многие могут не захотеть слышать. Те из вас, кто дает ответы, которые направляют автора вопроса к "обходному пути", оказывают серьезную медвежью услугу сообществу. Тестирование является основной частью всех инженерных дисциплин, вы не хотели бы покупать автомобиль, который не был должным образом протестирован и проверен осмысленно, так почему же кто-то захочет купить или использовать программное обеспечение, которое тестируется плохо? В любом случае, причина, по которой люди делают это, заключается в том, что последствия плохо протестированного программного обеспечения ощущаются не сразу, и мы обычно не связываем их с телесными повреждениями. Это очень опасное восприятие, которое будет трудно изменить, но мы несем ответственность за поставку безопасных продуктов независимо от того, что даже руководство заставляет нас делать это. Думаю, что Equifax взломать...
Мы должны стремиться к созданию среды, которая поощряет хорошие методы разработки программного обеспечения, это не означает, что мы должны подвергать остракизму слабых/ленивых среди нас, которые не относятся к своему делу серьезно, а скорее создать статус-кво подотчетности и саморефлексии, которое бы поощряло каждого стремиться к росту, умственно и умело. Я все еще учусь, и у меня могут быть неправильные представления/мнения, но я твердо верю, что мы должны быть ответственными за хорошие практики и избегать безответственных взломов или обходных путей для проблем.
Что делать, если ваши тестовые классы находятся в том же пакете, что и класс, который должен быть протестирован?
Но в другом каталоге, конечно, src
и classes
для вашего исходного кода, test/src
и test/classes
для ваших тестовых классов. И пусть classes
и test/classes
находятся в вашем пути к классам.
В С# вы могли бы использовать System.Reflection
, хотя в Java я не знаю. Хотя я чувствую желание ответить на это так или иначе, так как если вы "почувствуете, что вам нужно unit test частные методы", то я предполагаю, что есть что-то еще, что неправильно...
Я бы серьезно подумал о том, чтобы снова взглянуть на мою архитектуру свежими глазами...
Моя команда и я используем Typemock, у которого есть API, который позволяет вам подделывать непубличные методы. Недавно они добавили возможность подделывать невидимые типы и использовать XUnit.
Вы можете использовать PowerMockito для установки значений возврата для частных полей и частных методов, которые вызываются/используются в приватном методе, который вы хотите проверить:
Eg. Установка возвращаемого значения для частного метода:
MyClient classUnderTest = PowerMockito.spy(new MyClient());
//Set expected return value
PowerMockito.doReturn(20).when(classUnderTest, "myPrivateMethod", anyString(), anyInt());
//This is very important otherwise it will not work
classUnderTest.myPrivateMethod();
//Setting private field value as someValue:
Whitebox.setInternalState(classUnderTest, "privateField", someValue);
Затем, наконец, вы можете проверить свой закрытый метод под тестированием, возвращая правильное значение на основе заданных значений выше:
String msg = Whitebox.invokeMethod(obj, "privateMethodToBeTested", "param1");
Assert.assertEquals(privateMsg, msg);
Или
Если частный метод classUnderTest не возвращает значение, но он устанавливает другое личное поле, вы можете получить это значение частного поля, чтобы убедиться, что оно установлено правильно:
//To get value of private field
MyClass obj = Whitebox.getInternalState(classUnderTest, "foo");
assertThat(obj, is(notNull(MyClass.class))); // or test value
JML имеет синтаксис аннотации комментариев spec_public, который позволяет указать метод как открытый во время тестов:
private /*@ spec_public @*/ int methodName(){
...
}
Этот синтаксис обсуждается в http://www.eecs.ucf.edu/~leavens/JML/jmlrefman/jmlrefman_2.html#SEC12. Существует также программа, которая переводит спецификации JML в тесты JUnit. Я не уверен, насколько хорошо это работает или каковы его возможности, но он не кажется необходимым, поскольку JML является самодостаточной тестовой платформой.
Вы можете создать специальный публичный метод для прокси приватного метода для тестирования. Аннотация @TestOnly доступна из коробки при использовании IntelliJ. Недостатком является то, что если кто-то хочет использовать приватный метод в публичном контексте, он может это сделать. Но он будет предупрежден аннотацией и названием метода. На IntelliJ при этом появится предупреждение.
import org.jetbrains.annotations.TestOnly
class MyClass {
private void aPrivateMethod() {}
@TestOnly
public void aPrivateMethodForTest() {
aPrivateMethod()
}
}
Если у вас есть случай, когда вам действительно нужно протестировать закрытый метод/класс и т.д. Напрямую, вы можете использовать рефлексию, как уже упоминалось в других ответах. Однако, если дело доходит до этого, вместо непосредственного обращения к рефлексии, я бы предпочел использовать классы util, предоставляемые вашей средой. Например, для Java у нас есть:
ReflectionUtils Spring Framework
ReflectionUtils JUnit
Что касается их использования, вы можете найти множество статей в Интернете. Вот тот, который мне особенно понравился:
Groovy имеет ошибка/функция, с помощью которой вы можете ссылаться на частные методы, как если бы они были общедоступными. Поэтому, если вы можете использовать Groovy в своем проекте, это вариант, который вы можете использовать вместо отражения. Посмотрите эту страницу для примера.
Я тестирую только открытый интерфейс, но, как мне известно, я защищаю определенные частные методы, поэтому я могу полностью издеваться над ними или добавлять дополнительные шаги, специфичные для тестирования модулей. Общий случай заключается в подключении флагов, которые я могу установить из unit test, чтобы сделать определенные методы преднамеренно, чтобы исключение могло проверять пути ошибок; код запуска исключения только в тестовом пути в переопределенной реализации защищенного метода.
Я минимизирую необходимость в этом, и я всегда документирую точные причины, чтобы избежать путаницы.