获取mock的file.read()的实际返回值

19 投票
3 回答
33177 浏览
提问于 2025-04-17 09:37

我正在使用python-mock来模拟打开文件的操作。我希望能通过这种方式传入一些假数据,这样我就可以验证read()是否被调用,同时在测试时不需要真的去访问文件系统。

这是我目前的代码:

file_mock = MagicMock(spec=file)
file_mock.read.return_value = 'test'

with patch('__builtin__.open', create=True) as mock_open:
    mock_open.return_value = file_mock

    with open('x') as f:
        print f.read()

运行后得到的结果是<mock.Mock object at 0x8f4aaec>,而不是我预期的'test'。我在构建这个模拟的时候哪里出错了呢?

补充:

看起来像这样:

with open('x') as f:
     f.read()

和这样:

f = open('x')
f.read()

是不同的对象。把模拟作为上下文管理器使用时,它会返回一个新的Mock对象,而直接调用它则返回我在mock_open.return_value中定义的内容。有什么想法吗?

3 个回答

2

在@tbc0的回答基础上,为了支持Python 2和3(同时测试这两个版本对将Python 2迁移到3很有帮助):

import sys
module_ = "builtins"
module_ = module_ if module_ in sys.modules else '__builtin__'

try:
    import unittest.mock as mock
except (ImportError,) as e:
    import mock 

with mock.patch('%s.open' % module_, mock.mock_open(read_data='test')):
    with open('/dev/null') as f:
        print(f.read())
31

在Python 3中,使用的模式很简单:

>>> import unittest.mock as um
>>> with um.patch('builtins.open', um.mock_open(read_data='test')):
...     with open('/dev/null') as f:
...         print(f.read())
...
test
>>>

(是的,你甚至可以模拟/dev/null,让它返回文件内容。)

14

这听起来是一个很好的场景,可以使用一个叫做 StringIO 的对象,它已经具备了文件的功能。你可以这样做:file_mock = MagicMock(spec=file, wraps=StringIO('test'))。或者,你也可以让你的函数接受一个类似文件的对象,然后传入一个 StringIO,这样就不需要去做那些麻烦的修改了。

你有没有看过模拟(mock)的文档呢?

http://www.voidspace.org.uk/python/mock/compare.html#mocking-the-builtin-open-used-as-a-context-manager

撰写回答