2016-12-04 10 views
1

파일 경로를 취하고 파일 내용 중 일부를 반환하는 구문 분석 함수의 유닛 테스팅을 시도하고 있습니다. 테스트 목적으로 이러한 함수 문자열을 전달할 수 있기를 원합니다.Unittesting - 파일 경로를 StringIO 객체로 바꿉니다.

나는 csv.reader() StringIO 또는 file_handle (예 : csv.reader (StringIO ("my, data") 또는 csv.reader (open (file))) 오픈 (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)