2014-10-16 4 views
4

У меня есть класс, который имеет довольно сложную статическую инициализацию. Я читаю файлы из каталога, затем разбираю эти json-файлы, сопоставляя их с объектами и заполняя список. Вы можете себе представить, что могут возникнуть некоторые исключения, и мне нужно покрыть и проверить эти кодовые обозначения. Проблема в том, что эта статическая инициализация выполняется только один раз/файл Testcase. решения я кулачок через:Каков наилучший способ тестирования статического инициализатора класса?

  • нового файл тестового для каждого поведения
  • выгрузить статический класс
  • нового JVM

Я не увлечен этими вариантами, не есть что-то лучше ?

+1

Возможный дубликат [Как заставить статический блок работать в каждом методе тестирования?] (Http://stackoverflow.com/questions/5784277/how-to-force-the-static-block-running-in-each -test-method) – icza

ответ

3

Важным фактором в модульном тестировании является структурирование вашего кода, чтобы он был подходящим для тестирования.

К сожалению, как вы выяснили, код с чрезмерной статической инициализацией, выполняющей сложные операции ввода-вывода, не является проверенной структурой.

Помимо статической инициализации, это похоже на то, что код нарушает single-responsibility principle так же, как класс загружает себя из внешнего источника и, как правило, имеет какое-то другое применение.

Итак, вы получили некоторые рефакторинга делать, например, если ваш код был выглядеть примерно так (JSON синтаксического анализа, заменен CSV разбора для ясности):

public MyClass 
{ 
    private static List<MyObject> myObjects = new ArrayList<>(); 

    static 
    { 
    try 
    { 
     try (BufferedReader reader = new BufferedReader(new FileReader(myfile.csv)) 
     { 
     String line; 

     while ((line = reader.readLine()) != null) 
     { 
      String[] tokens = line.split(","); 
      myObjects.add(new MyObject(tokens[0], tokens[1], tokens[2])); 
     } 
     } 
    } 
    catch (IndexOutOfBoundsException e) 
    { 
     ... 
    } 
    catch (IOException e) 
    { 
     ... 
    } 
    } 
} 

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

public class MyObjectReader implements Closeable 
{ 
    private BufferredReader reader; 

    public MyObjectReader(Reader reader) 
    { 
    this.reader = new BufferredReader(reader); 
    } 

    public MyObject read() throws IOException 
    { 
    String line = reader.readLine(); 

    if (line != null) 
    { 
     String[] tokens = line.split(","); 

     if (tokens.length < 3) 
     { 
     throw new IOException("Invalid line encountered: " + line); 
     } 

     return new MyObject(tokens[0], tokens[1], tokens[2]); 
    } 
    else 
    { 
     return null; 
    } 
    } 

    public void close() throws IOException 
    { 
    this.reader.close(); 
    } 
} 

MyObjectReader класса вполне проверяем и, что важно не полагаться на файлы или других ресурсах, присутствуя, так Вы можете проверить это следующим образом:

public MyObjectReaderTest 
{ 
    @Test 
    public void testRead() throws IOException 
    { 
    String input = "value1.1,value1.2,value1.3\n" + 
     "value2.1,value2.2,value2.3\n" + 
     "value3.1,value3.2,value3.3"; 

    try (MyObjectReader reader = new MyObjectReader(new StringReader(input))) 
    { 
     assertEquals(new MyObject("value1.1", "value1.2", "value1.3"), reader.read()); 
     assertEquals(new MyObject("value2.1", "value2.2", "value2.3"), reader.read()); 
     assertEquals(new MyObject("value3.1", "value3.2", "value3.3"), reader.read()); 
     assertNull(reader.read()); 
    } 
    } 

    @Test(expected=IOException.class) 
    public void testReadWithInvalidLine() throws IOException 
    { 
    String input = "value1.1,value1.2"; 

    try (MyObjectReader reader = new MyObjectReader(new StringReader(input))) 
    { 
     reader.read(); 
    } 
    } 
} 

Не видя кода или зная формат файла, трудно расширить, но, надеюсь, вы получите суть.

Наконец, ваша статическая инициализация будет тогда просто:

public MyClass 
{ 
    private static List<MyObject> myObjects = new ArrayList<>(); 

    static 
    { 
    loadMyObjects(new FileReader("myfile.csv")); 
    } 

    /* package */ static void loadMyObjects(Reader reader) 
    { 
    try 
    { 
     try (MyObjectReader reader = new new MyObjectReader(reader)) 
     { 
     MyObject myObject; 

     while ((myObject = reader.read()) != null) 
     { 
      myObjects.add(myObject); 
     } 
     } 
    } 
    catch (IOException e) 
    { 
     ... 
    } 
    } 
}  

Это может быть стоит испытывать счастливый путь здесь, но лично метод loadMyObjects теперь так просто я, вероятно, не будет беспокоить.

+0

Ничего себе, спасибо за этот тщательный и подробный ответ. Отличная идея - отдельный класс читателей для тяжелых задач IO. – JSONStatham

9

Если вы не можете избежать статического инициализатора, извлеките его тело в метод. Затем проверьте метод. Вы статический инициализатор будет выглядеть как static { myMethod(); }, который вряд ли можно сломать.

+0

Ник Холт сделал более подробный ответ, но основная идея такая же. Спасибо :) – JSONStatham

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