使用两个不同文件的类中的 Python 模拟内置“打开"

问题描述

当它们都使用上下文管理器时,我无法弄清楚如何模拟一个类中打开的两个文件.我知道如何使用这样的模拟模块为一个上下文管理的文件执行此操作:

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.

相关文章