Что я использую вместо Whitebox в Mockito 2.2 для установки полей?
При использовании Mockito 1.9.x я использовал Whitebox
для установки значений полей для "инъекции" mocks. Пример ниже:
@Before
public void setUp() {
eventHandler = new ProcessEventHandler();
securityService = new SecurityServiceMock();
registrationService = mock(RegistrationService.class);
Whitebox.setInternalState(eventHandler, "registrationService", registrationService);
Whitebox.setInternalState(eventHandler, "securityService", securityService);
}
Мне очень нравится этот подход, но теперь, когда я попытался перейти на Mockito
2.2.7
, я заметил (точнее, мой IDE заметил и сказал мне несколько раз), что Whitebox больше не было найдено в Mockito.
Я нашел одну альтернативу, которая может работать как замена, а это org.powermock.reflect.Whitebox
, проблема в том, что я получаю другую зависимость (Powermock), просто чтобы использовать Whitebox./p >
Powermock
также имеют класс с именем Whitebox
, но, к сожалению, он выглядит так, как будто он не может использоваться с Mockito 2.2.x
Есть ли какие-либо хорошие альтернативы в Mockito, которые я могу использовать для ввода "вручную" вручную, теперь, когда Whitebox
больше не доступен?
Решение
Я написал в комментарии в ответ на сообщение, сделанное из @JeffBowman. Короче говоря, я решил скопировать код WhiteBox и использовать его, поскольку он используется в большинстве тестовых примеров, и класс не имеет зависимостей от других классов. Это был самый быстрый путь для решения этой проблемы.
Примечание Решение, предлагаемое @bcody, является лучшей альтернативой, если вы используете spring, он не содержит дополнительного кода для вашего обслуживания. Я получил эту информацию допоздна:(
Ответы
Ответ 1
Обратите внимание, что Whitebox
всегда находился в пакете org.mockito.internal
. Помимо увеличения основного номера версии, обозначение internal
является подделкой, что пакет может подвергаться изменениям.
Если вы хотите сделать так, чтобы в вашем тесте было задано иначе недоступное поле, вы можете сделать это так же, как это делает setInternalState
, который только для идентификации поля в иерархии, вызовите setAccessible
на нем, а затем установите его. Полный код находится здесь на grepcode. Вы также можете изучить несколько других способов установки недоступных состояние в тестах.
public static void setInternalState(Object target, String field, Object value) {
Class<?> c = target.getClass();
try {
Field f = getFieldFromHierarchy(c, field); // Checks superclasses.
f.setAccessible(true);
f.set(target, value);
} catch (Exception e) {
throw new RuntimeException(
"Unable to set internal state on a private field. [...]", e);
}
}
Однако, в таких ситуациях, мой общий совет - прекратить борьбу с инструментами: Java четыре уровня инкапсуляции (public, protected, package, private) не обязательно достаточно гранулированы, чтобы выразить степень защиты, которую вы пытаетесь выразить, и часто гораздо проще добавить хорошо документированный метод инициализации или переопределить конструктор для переопределения зависимостей по мере того, как вы пытаетесь сделать рефлексивно. Если вы поместите свои тесты в один и тот же пакет Java, как и класс, который он тестирует, вы часто можете даже сделать поля или метод/конструктор частным пакетом, что также является хорошей причиной для установки параллельных исходных папок src
и tests
(и т.д.), которые представляют две половины одного и того же пакета Java.
Хотя некоторые рассматривают этот дополнительный метод или конструктор как "загрязнение API", я рассматриваю его вместо этого как кодирование требованиям одного из ваших самых важных потребителей вашего класса - собственного теста. Если вам нужен чистый внешний интерфейс, вы можете легко определить его отдельно, чтобы вы могли скрыть любые детали, которые вы хотите. Тем не менее, вы можете обнаружить, что вам нравится внедрять любую реальную или макетную реализацию прямо в ваш теперь более гибкий компонент, после чего вы можете захотеть взглянуть на шаблоны инъекций зависимостей или фреймворки.
Ответ 2
Если вы используете Spring (особенно для библиотеки spring -test), вы можете просто использовать ReflectionTestUtils.setField
вместо Whitebox.setInternalState
Ответ 3
Самый чистый, изощренный и самый портативный способ, не изобретая велосипед, - это использовать FieldUtils
Apache Commons. https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.html
Ответ на ваш вопрос будет
public static void setStaticFieldValue(
@NonNull final Class<?> clz,
@NonNull final String fieldName,
@NonNull final Object value) throws Exception {
final Field f = FieldUtils.getField(clz, fieldName, true);
FieldUtils.removeFinalModifier(f);
f.set(null, value);
}
Ответ 4
Вы можете использовать FieldSetter в Mockito2.x
import org.mockito.internal.util.reflection.FieldSetter;
FieldSetter.setField(eventHandler,eventHandler.getClass().getDeclaredField("securityService"), securityService);
Ответ 5
Здесь с API Fest- Reflective вы можете найти простой в использовании API для поддержки отражения. Это то, что я использую в качестве альтернативы Mockito Whiltebox.