Используя отражение для изменения статического заключительного File.separatorChar для поблочного тестирования?

А именно, я пытаюсь создать модульный тест на метод, который требует использования File.separatorChar создавать пути на окнах и Unix. Код должен работать на обеих платформах, и все же я получаю ошибки с JUnit, когда я пытаюсь изменить это статическое заключительное поле.

У кого-либо есть какая-либо идея, что продолжается?

Field field = java.io.File.class.getDeclaredField( "separatorChar" );
field.setAccessible(true);
field.setChar(java.io.File.class,'/');

Когда я делаю это, я добираюсь

IllegalAccessException: Can not set static final char field java.io.File.separatorChar to java.lang.Character

Мысли?

26
задан Stefan Kendall 18 March 2010 в 23:20
поделиться

6 ответов

Из документации к Field.set:

Если базовое поле является конечным, метод выбрасывает IllegalAccessException, если только setAccessible(true) не был успешным для этого поля и это поле не является статическим.

Итак, сначала кажется, что вам не повезло, поскольку File.separatorChar является статическим. Удивительно, но есть способ обойти это: просто сделайте статическое поле более не конечным с помощью отражения.

Я адаптировал это решение с javaspecialist.eu:

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);
}

Я протестировал его, и оно работает:

setFinalStatic(File.class.getField("separatorChar"), '#');
System.out.println(File.separatorChar); // prints "#"

Будьте крайне осторожны с этой техникой. Если отбросить разрушительные последствия, то следующее действительно работает:

setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"

Важное обновление: приведенное выше решениеработает не во всех случаях. Если поле становится доступным и читается через Reflection до того, как оно будет сброшено, возникает IllegalAccessException. Это не удается, поскольку API Reflection создает внутренние объекты FieldAccessor, которые кэшируются и используются повторно (см. реализацию java.lang.reflect.Field#acquireFieldAccessor(boolean)). Пример тестового кода, который не работает:

Field f = File.class.getField("separatorChar"); f.setAccessible(true); f.get(null);
// call setFinalStatic as before: throws IllegalAccessException
65
ответ дан 28 November 2019 в 06:28
поделиться

Просто используйте / везде при создании файлов. Я занимаюсь этим 13 лет, и у меня никогда не было проблем. Тестировать тоже нечего.

2
ответ дан 28 November 2019 в 06:28
поделиться

Вместо использования File.separatorChar объявите свой сервисный класс, назовем его PathBuilder или что-то в этом роде. У этого класса будет метод concatPaths (), который объединит два параметра (используя символ-разделитель ОС). Прелесть в том, что вы пишете этот класс, чтобы вы могли настроить его как хотите при модульном тестировании.

0
ответ дан 28 November 2019 в 06:28
поделиться

Вы можете взять исходный код для java.io.File и изменить его так, чтобы separatorChar и separator не были окончательными, и добавить метод setSeparatorChar, который обновляет оба из них, а затем включить скомпилированный класс в ваш bootclasspath.

0
ответ дан 28 November 2019 в 06:28
поделиться

Попробуйте вызвать экземпляр файла, а не экземпляр класса File

Например.

File file = ...;    
field.setChar(file,'/');

Вы также можете попробовать http://code.google.com/p/jmockit/ и имитировать статический метод FileSystem.getFileSystem (). (не знаю, можете ли вы имитировать статические переменные, обычно в этих хаках нет необходимости -> напишите oo-код и используйте «только» mockito)

2
ответ дан 28 November 2019 в 06:28
поделиться

Я понимаю, что это не дает прямого ответа на ваш вопрос, но Apache Commons FileNameUtils выполнит кроссплатформенное построение имени файла и может избавьте вас от написания собственного класса для этого.

1
ответ дан 28 November 2019 в 06:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: