Что я использую вместо Whitebox в Mockito 2.2 для установки полей?

при использовании Mockito 1.9.x я использую Whiteboxчтобы установить значения полей для "inject" mocks. Se пример ниже:

@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 больше не было в Мокито.

я нашел одну альтернативу, которая может работать в качестве замены, и это org.powermock.reflect.Whitebox проблема в том, что я получите другую зависимость (Powermock), просто чтобы использовать Whitebox.

Powermock также есть класс с именем Whitebox, но, к сожалению, похоже, что его нельзя использовать с Mockito 2.2.x

есть ли хорошие альтернативы в Mockito, которые я могу использовать вручную "вводить" поля, теперь, когда Whitebox больше не доступен?


решение

Я написал в комментарии в ответ на сообщение, сделанное @JeffBowman. Короче я решил скопировать код WhiteBox, и использовать это, так как он используется в большинстве тестовых случаев и класс не имеет зависимостей от других классов. Это был самый быстрый путь к решению этой проблемы.

Примечание решение, которое предлагает @bcody, является лучшей альтернативой, если вы используете spring,он не рекламирует дополнительный код для вас. Я получил эту информацию поздно : (

3 ответов


отметим, что 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 (etc), которые представляют две половины одного и того же пакета Java.

хотя некоторые рассматривают этот дополнительный метод или конструктор как "загрязнение API" , я вижу это вместо того, чтобы кодировать требования одного из самых важных потребителей вашего класса -свой собственный тест. Если вам нужен нетронутый внешний интерфейс, вы можете легко определить его отдельно, чтобы скрыть любые детали. Тем не менее, вы можете найти вас как возможность вводить любую реальную или имитационную реализацию непосредственно в ваш теперь более гибкий компонент, и в этот момент Вы можете посмотреть на шаблоны или фреймворки инъекций зависимостей.


Если вы используете Spring (в частности, библиотеку spring-test), вы можете просто использовать ReflectionTestUtils.setField вместо Whitebox.setInternalState


самый чистый, опрятный и большинство портативных способ без изобретения колеса-использовать Apache Commons'FieldUtils. https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/FileUtils.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);
}