【问题标题】:Mocking a file containing JSON data with mock.patch and mock_open使用 mock.patch 和 mock_open 模拟包含 JSON 数据的文件
【发布时间】:2019-03-04 20:17:15
【问题描述】:

我正在尝试测试一种需要在 Python 3.6 中使用 json.load 的方法。 经过几次尝试,我尝试“正常”运行测试(使用 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 和 ipython 中的 python 版本,json 库的版本。

我想知道为什么看起来相同的代码可以在一个地方工作 并且在另一个中不起作用。 或者至少是一个指向正确方向的指针,以了解为什么会发生这种情况。

【问题讨论】:

  • 你的标题和你的问题似乎没有太大的关系。你在说什么功能?类是单元测试吗?这里的问题与其他方法或函数无关。
  • 如果你有一个工作的测试函数,那么你忘了分享它,你可能没有使用new_callable或者已经包装了lambda 中的可调用对象。它不会是完全相同的patch(...) 配置。
  • 我忘记分享测试函数了。对此感到抱歉。我再次检查了它,你是对的,这是一个略有不同的补丁配置。也感谢您对问题的更正。我太累了,我什至写不直。
  • 是时候让自己睡一觉了,之后你会更有效率。 :-)

标签: python python-3.x python-unittest python-unittest.mock


【解决方案1】:

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()

【讨论】:

    猜你喜欢
    • 2023-02-26
    • 1970-01-01
    • 2019-06-01
    • 2015-06-04
    • 2016-07-10
    • 2014-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多