2016-01-06 2 views
1

Я пишу фрагмент кода python, который анализирует отформатированный файл в объект python. Файл может меняться, но на данный момент я работаю, основываясь на подмножестве того, что может быть в файле, и надеюсь, что тесты могут помочь мне расширить его для всех этих файлов.Написание тестов для функций парсера файлов модульным способом

Файл сам по себе состоит из заголовка, содержащего метаданные, за которым следуют несколько блоков данных.

[general header, describes length of header 1 & header 2] 
[header describing data block 1] 
[header describing data block 2] 
[data block 1] 
[data block 2] 

В настоящее время мой код изложен следующим образом

with datafile as open(filename, 'r'): 
    gen_header_obj = parse_gen_header(datafile) 
    header1_obj = parse_header1(datafile, gen_header_obj.header1_len) 
    header2_obj = parse_header2(datafile, gen_header_obj.header2_len) 
    data1_obj = parse_data1(datafile, header1_obj.datalen) 
    data2_obj = parse_data2(datafile, header2_obj.datalen) 

Где каждый синтаксический анализ * Функция (файл) вызывает file.readline() несколько раз, в зависимости от размера заданной длины данных.

В идеале у меня было бы как минимум 5 отдельных тестов, где я предоставляю поддельную часть файла и вижу, правильно ли он получает информацию. Кроме того, в этом случае части данных довольно большие (мегабайты).

Можно ли написать тесты, которые напоминают следующее?

class TestParser(unittest.TestCase) 
    filename = 'locally_stored_file.txt' 

    def setUp(self): 
     self.file = open(filename, 'r') 

    def tearDown(self): 
     self.file.close() 

    def test_gen_header_parse(self): 
     result = parse_gen_header(datafile) 
     self.header1_len = result.header1_len 
     self.header2_len = result.header2_len 
     expected = ... 
     assertIsEqual(result, expected) 

    def test_header1_parse(self): 
     # datafile.seek() is left of from test_gen_header_parse 
     result = parse_header1(datafile, self.header1_len) 
     self.data1_len = result.data1_len 
     expected = ... 
     assertIsEqual(result, expected) 

    def test_header2_parse(self): 
     # datafile.seek() is left of from test_header1_parse 
     result = parse_header2(datafile, self.header2_len) 
     self.data2_len = result.data2_len 
     expected = ... 
     assertIsEqual(result, expected) 

    def test_data1_parse(self): 
     # datafile.seek() is left of from test_header2_parse 
     result = parse_data1(datafile, self.data1_len) 
     expected = ... 
     assertIsEqual(result, expected) 

    def test_data2_parse(self): 
     # datafile.seek() is left of from test_data1_parse 
     result = parse_data2(datafile, self.data2_len) 
     expected = ... 
     assertIsEqual(result, expected) 

    # Some code to force the tests to run sequentially as laid out above 

Как вы можете видеть, что я пытаюсь написать пять тестов, раздельные которые, мы надеемся, FaiL индивидуально, если что-то ломается в будущем. Однако я не могу проверить parse_header2 без запуска parse_gen_header и parse_header1 заранее.

Не уверен, есть ли лучший способ приблизиться к этому.

+0

Вы можете изменить формат файла данных для облегчения тестирования, или она определяется кем-то/что-то еще, и вы должны адаптировать свой код к ней? Потому что я бы действительно посоветовал ввести некоторые стандартные флаги в тело файла для обозначения начальных и конечных блоков заголовка/данных, однако это может быть невозможно в вашем случае. – Lav

+0

Это выход из исполняемой программы, более конкретно это форматированный файл данных для нейтронных сечений, в котором формат данных зависит от нескольких физических свойств (изотоп, метастат, доступные измерения). – abnowack

ответ

2

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

class TestParser(unittest.TestCase) 
    filename = 'locally_stored_file.txt' 
    expected_gen_header_length = 42 # The correct number it should be 
    expected_header1_length = 42 # The correct number it should be 
    # and lengths of the other things 

    def test_gen_header_parse(self): 
     with datafile = open(filename, 'r'): 
      result, len_header = parse_gen_header(datafile) # output len_header if you want to do an assert for it 
      self.header1_len = result.header1_len 
      self.header2_len = result.header2_len 
      expected = ... 
      assertIsEqual(self.expected_gen_header_length, len_gen_header) 
      assertIsEqual(result, expected) 

    def test_header1_parse(self): 
     with datafile = open(filename, 'r'): 
      # Force datafile.seek() to begin after gen_header 
      datafile.seek(self.expected_gen_header_length) 

      result = parse_header1(datafile, self.expected_header1_length) 
      self.data1_len = result.data1_len 
      expected = ... 
      assertIsEqual(result, expected) 

    # and so on .... 
+0

Вы уверены, что байты смещения в текстовом файле - хорошая идея? Смещения линий будут иметь больше смысла, но поскольку это текстовый файл (и, следовательно, потенциально редактируемый вручную), я бы не стал им доверять. – Lav

+0

Спасибо, +1, потому что это подход, который я мог бы принять, где ожидаемые длины заголовков и т. Д. Могут быть выведены из каждого тестового файла. Подумав об этом в одночасье, я решил использовать макетные файлы, позже опубликую ответ. – abnowack

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