首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >定制迭代的unittest.mock.mock_open

定制迭代的unittest.mock.mock_open
EN

Stack Overflow用户
提问于 2014-07-16 19:40:27
回答 3查看 10K关注 0票数 28

我应该如何定制unittest.mock.mock_open来处理这段代码?

代码语言:javascript
运行
复制
file: impexpdemo.py
def import_register(register_fn):
    with open(register_fn) as f:
        return [line for line in f]

我的第一次尝试是尝试read_data

代码语言:javascript
运行
复制
class TestByteOrderMark1(unittest.TestCase):
    REGISTER_FN = 'test_dummy_path'
    TEST_TEXT = ['test text 1\n', 'test text 2\n']

    def test_byte_order_mark_absent(self):
        m = unittest.mock.mock_open(read_data=self.TEST_TEXT)
        with unittest.mock.patch('builtins.open', m):
            result = impexpdemo.import_register(self.REGISTER_FN)
            self.assertEqual(result, self.TEST_TEXT)

这失败了,大概是因为代码没有使用read、readline或readline。unittest.mock.mock_open的documentation说:"read_data是文件句柄的read()、readline()和readlines()方法返回的字符串。对这些方法的调用将从read_data获取数据,直到数据被耗尽。这些方法的模拟非常简单。如果您需要更多地控制提供给测试代码的数据,您将需要为自己定制此模拟。默认情况下,read_data是一个空字符串。“

因为文档没有给出需要什么样的定制的提示,所以我尝试了return_valueside_effect。两种方法都不起作用。

代码语言:javascript
运行
复制
class TestByteOrderMark2(unittest.TestCase):
    REGISTER_FN = 'test_dummy_path'
    TEST_TEXT = ['test text 1\n', 'test text 2\n']

    def test_byte_order_mark_absent(self):
        m = unittest.mock.mock_open()
        m().side_effect = self.TEST_TEXT
        with unittest.mock.patch('builtins.open', m):
            result = impexpdemo.import_register(self.REGISTER_FN)
            self.assertEqual(result, self.TEST_TEXT)
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-07-16 19:42:09

mock_open()对象确实没有实现迭代。

如果您没有将文件对象用作上下文管理器,则可以使用:

代码语言:javascript
运行
复制
m = unittest.mock.MagicMock(name='open', spec=open)
m.return_value = iter(self.TEST_TEXT)

with unittest.mock.patch('builtins.open', m):

现在open()返回一个迭代器,它可以像文件对象一样直接迭代,它也可以与next()一起工作。但是,它不能用作上下文管理器。

您可以将其与mock_open()结合使用,然后在返回值上提供__iter____next__方法,增加的好处是mock_open()还添加了用作上下文管理器的先决条件:

代码语言:javascript
运行
复制
# Note: read_data must be a string!
m = unittest.mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: self
m.return_value.__next__ = lambda self: next(iter(self.readline, ''))

这里的返回值是从file对象(Python2)或in-memory file objects (Python3)指定的MagicMock对象,但只有readwrite__enter__方法被清除。

以上在Python2中不起作用,因为a) Python2期望next存在,而不是__next__;b) next在模拟中没有被视为特殊的方法(正确地如此),所以即使你在上面的例子中将__next__重命名为next,返回值的类型也不会有next方法。在大多数情况下,让file对象生成一个可迭代的而不是一个迭代器就足够了:

代码语言:javascript
运行
复制
# Python 2!
m = mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: iter(self.readline, '')

任何使用iter(fileobj)的代码都将正常工作(包括for循环)。

有一个旨在弥补这一差距的open issue in the Python tracker

票数 37
EN

Stack Overflow用户

发布于 2017-01-15 07:37:23

从Python3.6开始,由unittest.mock_open方法doesn't support iteration返回的模拟类文件对象。这个bug是在2014年报告的,到2017年它仍然是开放的。

因此,像这样的代码会默默地产生零迭代:

代码语言:javascript
运行
复制
f_open = unittest.mock.mock_open(read_data='foo\nbar\n')
f = f_open('blah')
for line in f:
  print(line)

您可以通过向mocked对象添加一个返回正确行迭代器的方法来解决此限制:

代码语言:javascript
运行
复制
def mock_open(*args, **kargs):
  f_open = unittest.mock.mock_open(*args, **kargs)
  f_open.return_value.__iter__ = lambda self : iter(self.readline, '')
  return f_open
票数 10
EN

Stack Overflow用户

发布于 2018-05-02 14:55:17

我找到了以下解决方案:

代码语言:javascript
运行
复制
text_file_data = '\n'.join(["a line here", "the second line", "another 
line in the file"])
with patch('__builtin__.open', mock_open(read_data=text_file_data), 
create=True) as m:
    # mock_open doesn't properly handle iterating over the open file with for line in file:
    # but if we set the return value like this, it works.
    m.return_value.__iter__.return_value = text_file_data.splitlines()
    with open('filename', 'rU') as f:
        for line in f:
            print line
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24779893

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档