嘲笑 "with open()"

Mocking "with open()"

我正在尝试对从文件中读取行并进行处理的方法进行单元测试。

with open([file_name], 'r') as file_list:
    for line in file_list:
        # Do stuff

我尝试了其他问题中描述的几种方法,但其中 none 似乎适用于这种情况。我不太明白 python 如何将文件对象用作线上的可迭代对象,它在内部使用 file_list.readlines() ?

这种方式不行:

    with mock.patch('[module_name].open') as mocked_open: # also tried with __builtin__ instead of module_name
        mocked_open.return_value = 'line1\nline2'

我得到了一个

AttributeError: __exit__

可能是因为 with 语句有这个关闭文件的特殊属性?

此代码使 file_list 成为 MagicMock。我如何在此 MagicMock 上存储数据以对其进行迭代?

with mock.patch("__builtin__.open", mock.mock_open(read_data="data")) as mock_file:

此致

mock_open 的 return 值(直到 Python 3.7.1)不提供有效的 __iter__ 方法,这可能使其不适合测试代码遍历打开的文件对象。

相反,我建议重构您的代码以获取已打开的类文件对象。也就是说,而不是

def some_method(file_name):
    with open([file_name], 'r') as file_list:
        for line in file_list:
            # Do stuff

...

 some_method(file_name)

写成

def some_method(file_obj):
    for line in file_obj:
        # Do stuff

...

with open(file_name, 'r') as file_obj:
    some_method(file_obj)

这将一个必须执行 IO 的函数变成了一个简单地迭代 any 类文件对象的纯 (r) 函数。要测试它,您不需要模拟 open 或以任何方式访问文件系统;只需创建一个 StringIO 对象用作参数:

def test_it(self):
    f = StringIO.StringIO("line1\nline2\n")
    some_method(f)

(如果你仍然觉得有必要编写和测试像

这样的包装器
def some_wrapper(file_name):
    with open(file_name, 'r') as file_obj:
        some_method(file_obj)

请注意,您不需要模拟打开来做任何特别的事情。您单独测试 some_method,因此测试 some_wrapper 唯一需要做的就是验证 open 的 return 值是否传递给了 some_methodopen,在这种情况下,可以是没有特殊行为的普通旧模拟。)