Ответ 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);
}
}
Обновление Вышеупомянутое решение будет работать только для тех констант, которые инициализируются в статическом блоке. При одновременном объявлении и инициализации константы может произойти, что компилятор строит ее, после чего игнорируется любое изменение исходного значения.