2016-12-04 2 views
1

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

Я понимаю, что я могу pas csv.reader() либо StringIO, либо file_handle (например, csv.reader (StringIO («my, data») или csv.reader (open (файл))), но я могу ' я вижу способ, которым я могу передать объект StringIO вместо путь к файлу, так как open (StringIO («my, data»)) терпит неудачу. В равной степени я хочу иметь логику открытия/закрытия файла в этих методах синтаксического анализа, а не в основная часть моего кода, как бы засоряют мой основной код, а также означает, что я должен переписать все интерфейсы ввода-вывода файлов

кажется, мой выбор:

  1. Перепишите все EXI sting-код, чтобы он передавал дескрипторы файлов в функции синтаксического анализа - это настоящая боль!
  2. Используйте mock.patch() для замены метода open() - это должно работать, но кажется более сложным, чем эта задача должна требовать!
  3. Сделайте то, о чем я еще не думал, но я убежден, что он должен существовать!

 
    import csv 
    def parse_file(input): 
     with open(input, 'r') as f: 
      reader = csv.reader(f) 
      output = [] 
      for row in reader: 
       #Do something complicated 
       output.append(row) 
      return output

import unittest class TestImport(unittest.TestCase): def test_read_string(self): string_input = u"a,b\nc,d\n" output = read_file(string_input) self.assertEqual([['a', 'b'], ['c', 'd']], output) def test_read_file(self): filename = "sample_data.csv" output = read_file(filename) self.assertEqual([['a', 'b'],['c', 'd']], output)

+0

Почему бы не просто написать тестовые чехлы на жесткий диск и передать им пути? –

+0

Вот как работает данный код: я стараюсь избегать этого, потому что: ** A ** Я не хочу, чтобы отслеживать множество действительно небольших текстовых файлов ** B ** Разбор имеет множество опций конфигурации - мне легко манипулировать строками в коде, чтобы имитировать их, но для этого с файлом потребуются десятки, что делает мою сборку «беспорядочной». – David258

ответ

2

Вы можете использовать temporary files.

Если вы действительно не хотите использовать жесткий диск, вы можете использовать StringIO для замены файлов и переопределить встроенную open функцию, например, так:

import StringIO 
import csv 

#this function is all you need to make your code work with StringIO objects 
def replaceOpen(): 
    #the next line redefines the open function 
    oldopen, __builtins__.open = __builtins__.open, lambda *args, **kwargs: args[0] if isinstance(args[0], StringIO.StringIO) else oldopen(*args, **kwargs) 

    #these methods below have to be added to the StringIO class 
    #in order for the with statement to work 
    StringIO.StringIO.__enter__ = lambda self: self 
    StringIO.StringIO.__exit__ = lambda self, a, b, c: None 

replaceOpen() 

#after the re-definition of open, it still works with normal paths 
with open(__file__, 'rb') as f: 
    print f.read(16) 

#and it also works with StringIO objects 
sio = StringIO.StringIO('1,2\n3,4') 
with open(sio, 'rb') as f: 
    reader = csv.reader(f) 
    output = [] 
    for row in reader: 
     output.append(row) 
    print output 

Это выходы:

import StringIO 
[['1', '2'], ['3', '4']] 
+0

Спасибо за указатели. У меня это работает, используя макет сейчас :) – David258

0

Если вы не хотите изменять интерфейс, чтобы принимать открытые файловые объекты, такие как StringIO, посмотрите на testfixtures module. Я использовал его для управления файлами и каталогами для модульных тестов, хотя обычно предпочитаю передавать объекты StringIO.

Если вам это не нравится, то исправление open() звучит как разумная стратегия. Я сам не пробовал.

0

Для других, которые искали это в будущем, я смог использовать Mock для этого достаточно эффективно.

---- module: import_data.py ----- 

import csv 

def read_file(input): 
    with open(input, 'r') as f: 
     reader = csv.reader(f) 
     output = [] 
     for row in reader: 
      #Do something complicated 
      output.append(row) 
     return output 

---- Unittests ---- 

import unittest 
from io import StringIO 
from mock import patch 
from import_data import read_file 

class TestImport(unittest.TestCase): 

    @patch('import_data.open') 
    def test_read_string(self, mock_file): 
     mock_file.return_value = StringIO(u"a,b\nc,d") 
     output = read_file(None) 
     self.assertEqual([['a', 'b'], ['c', 'd']], output) 


    def test_read_file(self): 
     filename = "sample_data.csv" 
     output = read_file(filename) 
     self.assertEqual([['a', 'b', 'c'],['d', 'e', 'f']], output) 
Смежные вопросы