Подтвердить что ты не робот

PowerMock: макет частной статической конечной переменной, конкретный пример

Каково абсолютное минимальное издевательство, которое необходимо выполнить, чтобы пройти этот тест?

код:

class PrivateStaticFinal {
    private static final Integer variable = 0;
    public static Integer method() { return variable + 1; }
}

Тест:

@RunWith(PowerMockRunner.class)
@PrepareForTest(PrivateStaticFinal.class)
class PrivateStaticFinalTest {
    @Test
    public void testMethod() {
        //TODO PrivateStaticFinal.variable = 100
        assertEquals(PrivateStaticFinal.method(), 101);
    }
}

related: Моментальные частные статические конечные переменные в классе тестирования (нет четкого ответа)

4b9b3361

Ответ 1

Отказ от ответственности: После многого поиска вокруг различных потоков я нашел ответ. Это можно сделать, но общий консенсус в том, что он не очень безопасен, но, видя, как вы это делаете ТОЛЬКО В ИСПЫТАНИЯХ ЕДИНИЦЫ, я думаю, что вы принимаете эти риски:)


Ответ не Mocking, так как большинство Mocking не позволяет взломать финал. Ответ немного более "взломанный", где вы фактически изменяете частное поле, когда Java вызывает, - это классы ядра java.lang.reflect.Field и java.lang.reflect.Modifier (отражение). Глядя на этот ответ, я смог собрать оставшуюся часть вашего теста без необходимости издеваться над тем, что решает вашу проблему.

Проблема с этим ответом заключается в том, что я запускался в NoSuchFieldException при попытке изменить variable. Помощь для этого заключалась в другом сообщении о том, как получить доступ к закрытому и не публичному полю.

Размышление о рефлексии/поле:

Так как Mocking не может обрабатывать final, вместо этого мы в конечном итоге делаем взлом в корень самого поля. Когда мы используем манипуляции Field (отражение), мы ищем конкретную переменную внутри класса/объекта. Когда Java найдет это, мы получим его "модификаторы", которые сообщают переменной, какие ограничения/правила имеют он как final, static, private, public и т.д. Мы находим правую переменную, а затем сообщите доступному коду, который позволяет нам изменять эти модификаторы. Как только мы изменили "доступ" в корне, чтобы мы могли манипулировать им, мы отключаем "окончательную" часть его. Затем мы можем изменить значение и установить его на все, что нам нужно.

Проще говоря, мы модифицируем переменную, чтобы мы могли изменять ее свойства, удаляя propety для final, а затем меняя значение, так как оно больше не final. Для получения дополнительной информации об этом просмотрите сообщение, откуда появилась идея.

Итак, шаг за шагом мы передаем переменную, которую хотим манипулировать, и...

// Mark the field as public so we can toy with it
field.setAccessible(true);
// Get the Modifiers for the Fields
Field modifiersField = Field.class.getDeclaredField("modifiers");  
// Allow us to change the modifiers
modifiersField.setAccessible(true);
 // Remove final modifier from field by blanking out the bit that says "FINAL" in the Modifiers
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// Set new value
field.set(null, newValue); 

Объединяя это все в новый SUPER ANSWER, вы получаете.

@RunWith(PowerMockRunner.class)
@PrepareForTest()
class PrivateStaticFinalTest {
    @Test
    public void testMethod(){
      try {
        setFinalStatic(PrivateStaticFinal.class.getDeclaredField("variable"), Integer.valueOf(100));
      } 
      catch (SecurityException e) {fail();}
      catch (NoSuchFieldException e) {fail();}
      catch (Exception e) {fail();}
      assertEquals(PrivateStaticFinal.method(), Integer.valueOf(101));
    }

    static void setFinalStatic(Field field, Object newValue) throws Exception {
        field.setAccessible(true);
        // remove final modifier from field
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, newValue);
    }
}

Обновление Вышеупомянутое решение будет работать только для тех констант, которые инициализируются в статическом блоке. При одновременном объявлении и инициализации константы может произойти, что компилятор строит ее, после чего игнорируется любое изменение исходного значения.