Python 3上下文管理器模拟单元测试



我有以下代码。

import yaml
def load_yaml_file(filename):
with open(filename, 'rt') as f:
data = yaml.load(f)
return data

有没有一种方法可以模拟open部分,使f变成'{"hello":"world"}',从而我可以断言数据被正确返回。

我试着用mock_open.return_value.__enter__.return_value = '{"hello":"world"}'模拟打开,但无法使其正常工作。

我正在使用pytest和mocker。

免责声明

  • 此解决方案不使用Mocker,但可以与pytest一起使用
  • 此解决方案适用于Python>=3.6

正如你所说的你正在使用Mocker,我假设你正在使用一个非常旧的代码库(<=2.6(。我强烈建议你将代码移植到任何版本>=3.6

由于Python 3.3 mock已集成到unittest.mock中的标准lib中,并且是旧mock包的一个克隆

mock-lib有一个名为mock_open的功能,它完全可以满足您的需要,并有一个完全符合您需求的示例。

with patch('__main__.open', mock_open(read_data='bibble')) as m:
with open('foo') as h:
result = h.read()
m.assert_called_once_with('foo')
assert result == 'bibble'

根据您的需要调整解决方案,您可以使用此示例

import yaml
from unittest.mock import patch, mock_open
def load_yaml_file(filename):
with open(filename, 'rt') as f:
data = yaml.load(f)
return data
with patch('__main__.open', mock_open(read_data='{"hello":"world"}')) as m:
res = load_yaml_file('foo')
assert res == {"hello":"world"}

您根本不需要设置__enter__。只需将要读取的数据作为read_data参数传递给mock_open():

mocked_open = mock.mock_open(read_data='{"hello":"world"}')
with mock.patch("yourmodule.open", mocked_open):
result = load_yaml_file("foobar.yaml")

演示:

>>> import yaml
>>> def load_yaml_file(filename):
...     with open(filename, 'rt') as f:
...         data = yaml.load(f)
...     return data
...
>>> from unittest import mock
>>> mocked_open = mock.mock_open(read_data='{"hello":"world"}')
>>> with mock.patch("__main__.open", mocked_open):
...     result = load_yaml_file("foobar.yaml")
...
>>> print(result)
{'hello': 'world'}
>>> mocked_open.mock_calls
[call('foobar.yaml', 'rt'),
call().__enter__(),
call().read(4096),
call().read(4096),
call().__exit__(None, None, None)]

最新更新