2014-11-06 4 views
2

둘 다 문맥 관리자를 사용할 때 클래스에서 두 개의 파일 열기를 모의하는 방법을 알아 내는데 어려움이 있습니다. 나는이 같은 모의 모듈을 사용하여 하나의 상황 관리 파일을 수행하는 방법을 알고Python 모의이 두 개의 다른 파일을 사용하는 클래스에서 '열기'

@patch('__builtin__.open') 
def test_interface_mapping(self, mock_config): 
     m = MagicMock(spec=file) 
     handle = m.return_value.__enter__.return_value 
     handle.__iter__.return_value = ('aa', 'bb') 

내 문제는 클래스가 같은 전화에 두 개의 서로 다른 파일을 열 때이 작업을 수행하는 방법이다. 필자의 경우, 클래스 __init__()은 두 개의 맵으로 파일을 미리로드합니다. 이 클래스는 다른 클래스에서 사용됩니다. IfAddrConfig 객체를 사용하는 다른 클래스를 미리로드 된 테스트 파일 내용에 대해 테스트 할 수 있도록 내 테스트 데이터를 제공하기 위해이 두 파일 로딩을 모의하고 싶습니다.

다음은 두 개의 파일을 __init__()에로드합니다. 두 파일 모두 테스트 대상 파일 내용을로드하려고 모의하고 싶습니다. getInterfaceMap()은 자주 호출되는 함수이므로 호출 할 때마다 파일을 파싱하고 파싱하지 않으므로 __init__()에 맵을 미리로드해야합니다. open()가 호출로

class IfAddrConfig(object): 
    def __init__(self): 
     # Initialize the static maps once since they require file operations 
     # that we do not want to be calling every time getInterfaceMap() is used 
     self.settings_map = self.loadSettings() 
     self.config_map = self.loadConfig() 

    def loadConfig(self): 
     config_map = defaultdict(dict) 
     with open(os.path.join('some_path.cfg'), 'r') as stream: 
      for line in stream: 
       # Parse line and build up config_map entries 
     return config_map 

    def loadSettings(self): 
     settings_map = {} 
     with open('another_path.cfg', 'r') as stream: 
      for line in stream: 
       # Parse line and build up settings_map entries 
     return settings_map 

    def getInterfaceMap(self, interface): 
     # Uses both the settings and config maps to finally create a composite map 
     # that is returned to called 
     interface_map = {} 
     for values in self.config_map.values(): 
      # Accesss self.settings_map and combine/compare entries with 
      # self.config_map values to build new composite mappings that 
      # depend on supplied interface value 
     return interface_map 

답변

4

당신은 당신의 패치 open 객체 (mock_open)의 side_effect 속성을 사용해야하며 __exit__ 방법에 대한 return_value을 설정하는 것을 잊지 마세요.

@patch('__builtin__.open', spec=open) 
def test_interface_mapping(self, mock_open): 
    handle1 = MagicMock() 
    handle1.__enter__.return_value.__iter__.return_value = ('aa', 'bb') 
    handle1.__exit__.return_value=False 
    handle2 = MagicMock() 
    handle2.__enter__.return_value.__iter__.return_value = ('AA', 'BB') 
    handle2.__exit__.return_value=False 
    mock_open.side_effect = (handle1, handle2) 
    with open("ppp") as f: 
     self.assertListEqual(["aa","bb"],[x for x in f]) 
    with open("ppp") as f: 
     self.assertListEqual(["AA","BB"],[x for x in f]) 

내가 그것을 Mock builtin 'open" function when used in contextlib

그래서 당신은 재 작성 할 수 있습니다 테스트와 같은

@patch('__builtin__.open', new_callable=mock_open, read_data="aa\nbb") 
def test_interface_mapping_new(self, mo): 
    handlers = (mo.return_value,mock_open(read_data="AA\nBB").return_value,) 
    mo.side_effect = handlers 
    with open("ppp") as f: 
     self.assertEqual("aa\nbb",f.read()) 
    with open("ppp") as f: 
     self.assertEqual("AA\nBB",f.read()) 

그리고 파이썬 3.4 당신이 할 수에서 할 수있는 훨씬 더 우아한 방법을 발견 [편집] 다른 것을 조롱하지 않고 readline(), readlines()도 사용하십시오.

+0

고마워요! 이 대답은 정확하고 도움이됩니다. – chromeeagle

+0

@Mark 유용한 경우 +1을 잊지 마세요 :) –

+0

Thank you! 제 질문은 달라졌습니다. 어떻게''f : line in f : ...' '와 같이''파일명을 열어서' ''내가 모의합니까? 불행히도'mock_open'은 반복에 대해서는 아무 것도하지 않습니다. –

2

당신은 두 개의 '파일'조롱을 만들 것, 모의 open 순서대로이 돌아갑니다.

@patch('__builtin__.open') 
def test_interface_mapping(self, mock_open): 
    handle1 = MagicMock('file1').__enter__.return_value 
    handle1.__iter__.return_value = ('aa', 'bb') 
    handle2 = MagicMock('file2').__enter__.return_value 
    handle2.__iter__.return_value = ('foo', 'bar') 
    mock_open.return_value.side_effect = (handle1, handle2) 

조롱 open() 통화를 호출 해 첫 handle1, handle2 결과 : side_effect attribute 당신이 그냥 할 수 있습니다. 그런 다음 어떤 객체는 호출에 대해 주어진 튜플을 반환하는 모의와 함께 호출되는 __enter__()에 응답합니다.

+0

는 왜 패치 않습니다는'__builtin __ open'을 만 (즉시 함수의 첫 번째 줄에'mock_open'을 재정의) 결과 모의 객체를 멀리 던져.? –

+0

@ ron.rothmanℝℝ : 좋은 질문입니다. 둘 다 할 필요가 없습니다. 내가 왜 그렇게했는지 (3 년이 거의 다름) 기억이 안납니다. 그래서 저는 두 번째 모의 작품을 삭제했습니다. –

1

파일 내용을 훨씬 더 제어해야하는 경우에는 래퍼 기능을 사용할 수 있습니다. 파일 내용에 따라 파일 내용을 원본 open과 동일하게 바꿉니다. elif에서

import unittest.mock as mock 


def my_open(filename): 
    if filename == 'file.txt': 
     content = "text file\ncontent" 
    elif filename == 'second.txt': 
     content = 'foobar' 
    else: 
     raise FileNotFoundError(filename) 
    file_object = mock.mock_open(read_data=content).return_value 
    file_object.__iter__.return_value = content.splitlines(True) 
    return file_object 

는 기존의 각 파일 경로에 대해 "파일의 내용을"로 설정 체인.

테스트 :

# standalone 
open_patch = mock.patch('__main__.open', new=my_open) 
open_patch.start() 

file = open('file.txt') 
assert file.read() == "text file\ncontent" 
file.close() 

open_patch.stop() 

#with statement 
with mock.patch('__main__.open', new=my_open): 
    with open('second.txt') as file: 
     assert file.read() == 'foobar' 

    # as iterable 
    with open('file.txt') as file: 
     assert ['text file\n', 'content'] == list(file) 

# function decorator 
@mock.patch('__main__.open', new=my_open) 
def test_patched_open(): 
    with open('second.txt') as file: 
     assert file.readline() == 'foobar' 

test_patched_open()