問題描述
當它們都使用上下文管理器時,我無法弄清楚如何模擬一個類中打開的兩個文件.我知道如何使用這樣的模擬模塊為一個上下文管理的文件執行此操作:
I am having trouble figuring out how to mock two file opens in a class when they both use context managers. I know how to do it for one context-managed file using the mock module like this:
@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 對象的其他類.
My problem is how to do this when a class opens two different files in the same call. In my case, the class __init__()
preloads the files into two maps. This class is used in other classes. I want to mock the loading of these two files to provide my test data so that the other classes that use the IfAddrConfig object can be tested against my preloaded test file content.
這是一個我正在努力使用的類的示例,它在 __init__()
中加載兩個文件,我想模擬這兩個文件以加載我的測試注入文件內容.getInterfaceMap() 是經常調用的函數,所以我不希望每次調用都加載和解析文件,因此在 __init__()
中預加載地圖一次.
Here's an example of the class I am struggling with that loads two files in __init__()
, both of which I want to mock to load my test injected file contents. getInterfaceMap() is the function that is called frequently so I do not want that to be loading and parsing the files every call, hence the reason for preloading the maps in __init__()
once.
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
推薦答案
你必須使用你的補丁 open
對象的 side_effect
屬性 (mock_open
) 并且不要忘記為 __exit__
方法設置 return_value
.
You must use side_effect
attribute of your patched open
object (mock_open
) and don't forget to set the return_value
for __exit__
method.
@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開放"在 contextlib 中使用時的函數
I found a much more elegant way to do it Mock builtin 'open" function when used in contextlib
所以你可以像這樣重寫測試
So you can rewrote test like
@patch('__builtin__.open', new_callable=mock_open, read_data="aa
bb")
def test_interface_mapping_new(self, mo):
handlers = (mo.return_value,mock_open(read_data="AA
BB").return_value,)
mo.side_effect = handlers
with open("ppp") as f:
self.assertEqual("aa
bb",f.read())
with open("ppp") as f:
self.assertEqual("AA
BB",f.read())
從 python 3.4 開始,您還可以使用 readline()、readlines() 而無需模擬其他任何內容.
And from python 3.4 you can use also readline(), readlines() without mocking anything else.
這篇關于使用兩個不同文件的類中的 Python 模擬內置“打開"的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!