【问题标题】:Mocking "with open()"模拟“使用 open()”
【发布时间】:2018-10-31 13:02:55
【问题描述】:

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

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

我尝试了其他问题中描述的几种方法,但似乎没有一种方法适用于这种情况。我不太明白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:

最好的问候

【问题讨论】:

  • 短语for x in y:通过调用iter(y)创建一个迭代器,它将尝试调用y.__iter__()来创建它。所以我建议通过一个模拟来拦截这个调用。
  • read_data 您将迭代的数据。
  • 这可能是您想要修改代码以使其更易于测试的情况。假设这是一个将文件名作为参数的函数,则改为使用文件 object。这使得调用者负责打开文件,但它也允许您传递 StringIO 对象作为测试参数。 (作为一般规则,将 I/O 尽可能推到代码的边缘会更容易测试。)
  • @chepner 我认为 read_data 仅适用于 file_obj.read() 方法,使用此方法让我找回了 read_data。因为在这段代码中它永远不会进入 for 循环,可能是因为它试图遍历 MagicMock 并且它不起作用。
  • 查看 mock_open 的文档,似乎直到 Python 3.7 才添加对 __iter__ 的支持。 (这对我来说似乎是一个巨大的疏忽,但我猜,迟到总比没有好。)

标签: python python-2.7 mocking with-statement


【解决方案1】:

mock_open 的返回值(直到 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 的函数转换为纯 (r) 函数,该函数只需迭代 any 类文件对象。要对其进行测试,您无需模拟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)

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 2011-08-11
    相关资源
    最近更新 更多