如何使用mock.patch模拟生成器

aks*_*tia 9 python generator nose python-mock python-unittest

我已经浏览了https://docs.python.org/3/library/unittest.mock-examples.html页面,我看到他们已经列出了如何模拟生成器的示例

我有一个代码,我调用生成器给我一组值,我保存为字典.我想在单元测试中模拟对这个生成器的调用.

我写了以下代码,但它不起作用.

我哪里错了?

In [7]: items = [(1,'a'),(2,'a'),(3,'a')]

In [18]: def f():
    print "here"
    for i in [1,2,3]:
        yield i,'a'

In [8]: def call_f():
   ...:     my_dict = dict(f())
   ...:     print my_dict[1]
   ...: 

In [9]: call_f()
"here"
a

In [10]: import mock


In [18]: def test_call_f():
    with mock.patch('__main__.f') as mock_f:
        mock_f.iter.return_value = items
        call_f()
   ....: 

In [19]: test_call_f()
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-19-33ca65a4f3eb> in <module>()
----> 1 test_call_f()

<ipython-input-18-92ff5f1363c8> in test_call_f()
      2     with mock.patch('__main__.f') as mock_f:
      3         mock_f.iter.return_value = items
----> 4         call_f()

<ipython-input-8-a5cff08ebf69> in call_f()
      1 def call_f():
      2     my_dict = dict(f())
----> 3     print my_dict[1]

KeyError: 1
Run Code Online (Sandbox Code Playgroud)

wim*_*wim 17

改变这一行:

mock_f.iter.return_value = items
Run Code Online (Sandbox Code Playgroud)

对此:

mock_f.return_value = iter(items)
Run Code Online (Sandbox Code Playgroud)

  • 小心这一点!当您多次调用该方法时,它将遍历可迭代中的项目。 (3认同)

Ale*_*aes 5

我还有另一种方法:

mock_f.__iter__.return_value = [items]
Run Code Online (Sandbox Code Playgroud)

这样你就可以真正模拟迭代器的返回值。

即使您正在模拟可迭代且具有方法的复杂对象(我的情况),这种方法也有效。

我尝试了所选的答案,但在我的情况下不起作用,只有当我嘲笑我解释的方式时才起作用

  • 很好的方法,但解释为什么你的答案与已经排除的答案不同和/或更好是一种很好的做法。 (2认同)

ger*_*rit 5

威姆斯回答

\n
mock_f.return_value = iter(items)\n
Run Code Online (Sandbox Code Playgroud)\n

只要你的模拟只被调用一次,就可以工作。在单元测试中,我们可能经常希望使用不同的参数多次调用一个函数或方法。在这种情况下,这将失败,因为在第一次调用时,迭代器将耗尽,因此在第二次调用时,它将立即引发异常StopIteration。通过Alexandre Paes 的回答,我得到了一个AttributeError: \'function\' object has no attribute \'__iter__\'何时我的模拟来自unittest.mock.patch.

\n

作为替代方案,我们可以创建一个 \xe2\x80\x9cfake\xe2\x80\x9d 迭代器并将其分配为side_effect

\n
@unittest.mock.patch("mymod.my_generator", autospec=True):\ndef test_my_func(mm):\n    from mymod import my_func\n    def fake():\n        yield from [items]\n    mm.side_effect = fake\n    my_func()  # which calls mymod.my_generator\n    my_func()  # subsequent calls work without unwanted memory from first call\n
Run Code Online (Sandbox Code Playgroud)\n

  • @buhtz `autospec` 确保模拟需要与原始版本相同的规范(输入参数)。“side_effect”是每次调用模拟时都会调用的函数或生成器。有关详细信息,请参阅 unittest.mock 文档。 (2认同)