2016-12-04 4 views
1

ファイルパスを取得し、ファイルの内容の一部を返す構文解析関数をユニットテストしようとしています。私はテスト目的のためにこれらの関数の文字列を渡すことができるようにしたいと思います。Unittesting - ファイルパスをStringIOオブジェクトに置き換える

StringIOまたはfile_handle(例:csv.reader StringIO( "my、data")またはcsv.reader(open(file))のいずれかをpas csv.reader()できますが、 open(StringIO( "my、data"))が失敗したので、ファイルパスの代わりにStringIOオブジェクトを渡すことができます。同様に、これらの解析メソッドではなく、これらの解析メソッドでファイルを開くそのように私のコードの主要な大部分は私のメインのコードを乱雑にしても、私はすべてのファイルIOインタフェースを再記述する必要が意味するであろう

私の選択肢があるようだ!

  1. リライトのすべてのEXIコードを解析してファイルハンドルを解析関数に渡すようにしてください - これは本当の苦痛です!
  2. open()メソッドを置き換えるにはmock.patch()を使用します。これはうまくいくはずですが、このタスクよりも複雑に思えます!
  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

これはコードが現在どのように動作しているかを示しています。私はこれを避けようとしています: ** **本当に小さなテキストファイルをたくさん追跡したくありません。 ** B **解析には設定オプションがたくさんあります - これらをシミュレートするためにコード内の文字列を操作するのは簡単ですが、ファイルでこれを行うには数十回の時間がかかります。 – David258

答えて

2

あなたはtemporary filesを使用することができます。

あなたは本当にハードディスクを使用しない場合は、そのように、あなたのファイルを交換し、組み込みopen機能を再定義したStringIOを使用することができます。

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) 
関連する問題