2016-08-13 2 views
1

У меня есть класс, какКак изменить значение «частной статической конечной строка Filepath» для модульного тестирования

public class MappingLoader 
    { 
    private static final String filepath = "/tmp/mapping.properties" // unix path of production system 
    private static Map<String,String> mapping = new HashMap<String,String>() 

    static 
    { 
     loadMappingFile() 
    } 


@VisibleForTesting  
static void loadMappingFile() 
    { 
     //reading properties files here 

    final Properties prop = new Properties(); 
     try (final InputStream input = Files.newInputStream(Paths.get(filepath))) 
     { 
      prop.load(input); 
     } 
     catch (final Exception e) 
     { 
      ... 
      ... 
      throw new RuntimeException(e); 
     } 

       //now load "mapping" from properties file 
       .... 
        .... 
     } 
    } 

Для тестирования, мне нужно изменить значение строки переменного «FilePath» таким образом, что оно должно принять развитие (например, c: \ project \ target \ mapping.properties)

Я пробовал силовые механизмы в юнитах, но он всегда выдает исключение и заканчивается.

аннотаций на уровне класса:

@RunWith(PowerMockRunner.class) 
@SuppressStaticInitializationFor("some.package.ClassWithStaticInit") 

и в тесте:

Whitebox.setInternalState(Mappingloader.class, "filepath", testfilepath); 
Mappingloader.loadMappingFile(); 

Я также попытался изменить это с помощью отражения, как указано в (Change private static final field using Java reflection), но он всегда бросает FileNotFoundException для " filepath "и не принимает измененный путь" testFilePath "

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

Если я удалю "throw new RuntimeException (e);" в исходном коде для меня работают силовые машины. но я хочу достичь этого без изменения исходного кода, либо через powermock, отражение api.

ответ

3

Ну, вы можете испытать удачу с Powermock; и это должно работать (возможно, если вы потратите еще несколько часов на чтение своей документации и проведение экспериментов); но честно: ваша проблема не тестируется. Ваша проблема в том, что вы создали непроверяемый код. И теперь вы пытаетесь использовать большой молоток для ремонта, чтобы «исправить», что ваш сломанный дизайн.

Вы видите при использовании статические методы и константы; люди думают, что они «экономят» на производительности (это правда, но на очень небольшой степени, что, вероятно, не имеет значения для 99,999% всех приложений); но они продолжают забывать, что использование статических приводит к напрямую связи различных функций. статический - ненормальность в хорошем дизайне OO; и их следует использовать с большой осторожностью.

Таким образом, в вашем случае, вы могли бы заменить все это с чем-то вдоль этих линий:

interface MappingLoader { 
    Map<String, String> loadMappingsFrom(String fileName); 
} 

class MappingLoaderImpl implements MappingLoader { 
    ... 

и вы видите, вдруг вы только дело с «реальными» интерфейсов и классов; и нестатические методы; и удивление: теперь вы можете полностью протестировать все это; и, скорее всего, вам даже не нужны фальшивые рамки. Вы просто создаете временный файл с сопоставлениями где-то; и затем вы убедитесь, что ваш класс impl дает вам сопоставления из этого файла. Zero mocking; нулевое тестирование внутренний детали реализации; всего несколько утверждений.

И еще одно преимущества: вверху весь ваш код клиента, который должен только быть с помощью интерфейса MappingLoader также может быть проверено. Поскольку обычные фреймворки, такие как EasyMock или Mockito, позволят вам издеваться над экземплярами этого интерфейса ... потому что: никаких статических вызовов больше нет!

Вот как вы изменяете значение частных конечных статических полей - не используя их!

(и если я сделал вам любопытно: смотреть this, чтобы узнать, как писать проверяемой код с самого начала)

+1

Отличный отклик! Я полностью согласен с тем, как разработать тестируемый код. И даже больше: как только у вас есть реальная, многоразовая и проверяемая абстракция для чтения любого файла свойств, нет возражений, чтобы статический фасад читал конкретный файл. (Если абстракция была полностью протестирована, нет необходимости проверять фасад.) –

+0

Добро пожаловать. – GhostCat

+0

@GhostCat, все, что вы сказали, хорошо думает и как оно должно быть. Но вы не сказали, как это может быть достигнуто, когда у разработчика уже есть такой код, и, возможно, это часть библиотеки, которая не может быть изменена. Конечно, разработчик должен следовать всем этим принципам, избегать использования статических и т. Д., И так далее, но когда проект запускается с нуля. Если код не покрывается тестом - внесите изменения, это не очень хорошая идея. –

1

Хотя я полностью согласен с @ GhostCat Ответным, я понимаю, что вы ищете решение, не предусматривающее для изменения исходного кода. Думали ли вы об изменении содержимого /tmp/mapping.properties перед запуском теста (и восстановить его позже)?

+0

Это простая и вполне разумная идея. –

+0

@ Rogério Спасибо, друг мой. –

0

static final String поля или любые поля полей примитивов final static не могут быть изменены во время выполнения. Если скорость честна, вы можете изменить эти поля, но ваше изменение не повлияет на код, который использует эти поля, потому что во время компиляции время ссылки заменяется значением.

Мое предложение: используйте static mock до Files.newInputStream(), а затем верните ByteArrayInputStream с ожидаемыми данными. В этом случае вы избежите хрупкой операции ввода-вывода диска, которая может повлиять на стабильность вашего теста.

+0

Диск IO (из локальной файловой системы, как в этом случае, по крайней мере) не является хрупкой операцией, и ее не следует издеваться. Всякий раз, когда мы запускаем Java-программу, в любом случае происходит много дискового ввода-вывода. –

Смежные вопросы