2016-12-04 25 views
1

我想單元測試解析函數,它需要一個文件路徑並返回一些文件內容。我希望能夠傳遞這些函數的數據串用於測試目的。我知道我可以通過csv.reader()或StringIO或file_handle(例如csv.reader(StringIO(「my,data」)或csv.reader(open(file))),但我可以' t看到一種方式,我可以通過一個StringIO對象來代替文件路徑,因爲open(StringIO(「my,data」))失敗。同樣,我想在這些解析方法中擁有文件打開/關閉邏輯,而不是我的代碼的主要大宗因爲這會弄亂我的主要代碼,同時也意味着我必須重新編寫所有的文件IO接口單元測試 - 用StringIO對象替換文件路徑

看來我的選擇是:

  1. 重寫所有EXI蜇代碼,以便它將文件句柄傳遞給解析函數 - 這真是一種痛苦!
  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

對於其他人在未來尋找這個我能夠使用模擬相當有效地做到這一點。

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