使用 mock.patch 和 mock_open 模拟包含 JSON 数据的文件
Mocking a file containing JSON data with mock.patch and mock_open
我正在尝试测试一种需要在 Python 3.6 中使用 json.load
的方法。
经过几次尝试,我尝试了 运行 测试 "normally"(使用 CLI 中通常的 unittest.main()),并在 iPython REPL 中进行了测试。
具有以下功能(为示例目的而简化)
def load_metadata(name):
with open("{}.json".format(name)) as fh:
return json.load(fh)
通过以下测试:
class test_loading_metadata(unittest2.TestCase):
@patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}'))
def test_load_metadata_with_disabled(self, filemock):
result = load_metadata("john")
self.assertEqual(result,{"disabled":True})
filemock.assert_called_with("john.json")
测试文件的执行结果,令人心碎:
TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'
在命令行中执行相同的操作时,给出了成功的结果。
我尝试了几种方法(用 with
作为装饰器进行修补),但我唯一能想到的是 unittest
库本身,以及它可能对干扰模拟和补丁。
还检查了 virtualenv 中 python 的版本和 ipython,json
库的版本。
我想知道为什么看起来相同的代码却在同一个地方工作
并且在另一个中不起作用。
或者至少是一个指向正确方向的指针,以理解为什么会发生这种情况。
json.load()
只是调用 fh.read()
,但 fh
不是 mock_open()
对象。这是一个 mock_open()()
对象,因为 new_callable
在修补以创建替换对象之前被 调用 :
>>> from unittest.mock import patch, mock_open
>>> with patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}')) as filemock:
... with open("john.json") as fh:
... print(fh.read())
...
<MagicMock name='open()().__enter__().read()' id='4420799600'>
不要使用 new_callable
,您不希望调用您的 mock_open()
对象!只需将其作为 new
参数传递给 @patch()
(这也是第二个位置参数,因此您可以在此处省略 new=
):
@patch('builtins.open', mock_open(read_data='{"disabled":True}'))
def test_load_metadata_with_disabled(self, filemock):
当用作 open()
函数时,您可以在其上调用 .read()
:
>>> with patch('builtins.open', mock_open(read_data='{"disabled":True}')) as filemock:
... with open("john.json") as fh:
... print(fh.read())
...
{"disabled":True}
new
参数是修补时将替换原始对象的对象。如果保留默认值,则使用 new_callable()
代替。你不想在这里 new_callable()
。
我正在尝试测试一种需要在 Python 3.6 中使用 json.load
的方法。
经过几次尝试,我尝试了 运行 测试 "normally"(使用 CLI 中通常的 unittest.main()),并在 iPython REPL 中进行了测试。
具有以下功能(为示例目的而简化)
def load_metadata(name):
with open("{}.json".format(name)) as fh:
return json.load(fh)
通过以下测试:
class test_loading_metadata(unittest2.TestCase):
@patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}'))
def test_load_metadata_with_disabled(self, filemock):
result = load_metadata("john")
self.assertEqual(result,{"disabled":True})
filemock.assert_called_with("john.json")
测试文件的执行结果,令人心碎:
TypeError: the JSON object must be str, bytes or bytearray, not 'MagicMock'
在命令行中执行相同的操作时,给出了成功的结果。
我尝试了几种方法(用 with
作为装饰器进行修补),但我唯一能想到的是 unittest
库本身,以及它可能对干扰模拟和补丁。
还检查了 virtualenv 中 python 的版本和 ipython,json
库的版本。
我想知道为什么看起来相同的代码却在同一个地方工作 并且在另一个中不起作用。 或者至少是一个指向正确方向的指针,以理解为什么会发生这种情况。
json.load()
只是调用 fh.read()
,但 fh
不是 mock_open()
对象。这是一个 mock_open()()
对象,因为 new_callable
在修补以创建替换对象之前被 调用 :
>>> from unittest.mock import patch, mock_open
>>> with patch('builtins.open', new_callable=mock_open(read_data='{"disabled":True}')) as filemock:
... with open("john.json") as fh:
... print(fh.read())
...
<MagicMock name='open()().__enter__().read()' id='4420799600'>
不要使用 new_callable
,您不希望调用您的 mock_open()
对象!只需将其作为 new
参数传递给 @patch()
(这也是第二个位置参数,因此您可以在此处省略 new=
):
@patch('builtins.open', mock_open(read_data='{"disabled":True}'))
def test_load_metadata_with_disabled(self, filemock):
当用作 open()
函数时,您可以在其上调用 .read()
:
>>> with patch('builtins.open', mock_open(read_data='{"disabled":True}')) as filemock:
... with open("john.json") as fh:
... print(fh.read())
...
{"disabled":True}
new
参数是修补时将替换原始对象的对象。如果保留默认值,则使用 new_callable()
代替。你不想在这里 new_callable()
。