Что я использую вместо 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.