【问题标题】:How to mock generators with mock.patch如何使用 mock.patch 模拟生成器
【发布时间】:2016-07-10 00:30:46
【问题描述】:

我浏览了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

【问题讨论】:

    标签: python generator nose python-unittest python-mock


    【解决方案1】:

    改变这一行:

    mock_f.iter.return_value = items
    

    到这里:

    mock_f.return_value = iter(items)
    

    【讨论】:

    • 小心这个!当您多次调用该方法时,它将遍历可迭代项中的项目。
    【解决方案2】:

    我有另一种方法:

    mock_f.__iter__.return_value = [items]
    

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

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

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

    【讨论】:

    • 不错的方法,但最好解释一下为什么您的答案与已经排除的答案不同和/或更好。
    【解决方案3】:

    Wims answer:

    mock_f.return_value = iter(items)
    

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

    作为替代方案,我们可以创建一个“假”迭代器并将其分配为side_effect

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-04
      • 1970-01-01
      • 1970-01-01
      • 2015-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-08
      相关资源
      最近更新 更多