简体   繁体   English

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

[英]How to mock generators with mock.patch

I have gone through the page https://docs.python.org/3/library/unittest.mock-examples.html and i see that they have listed an example on how to mock generators我浏览了https://docs.python.org/3/library/unittest.mock-examples.html页面,我看到他们列出了一个关于如何模拟生成器的示例

I have a code where i call a generator to give me a set of values that i save as a dictionary.我有一个代码,我在其中调用生成器来给我一组保存为字典的值。 I want to mock the calls to this generator in my unit test.我想在我的单元测试中模拟对这个生成器的调用。

I have written the following code and it does not work.我已经编写了以下代码,但它不起作用。

Where am i going wrong?我哪里出错了?

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

Change this line:改变这一行:

mock_f.iter.return_value = items

To this:对此:

mock_f.return_value = iter(items)

I have another approach:我有另一种方法:

mock_f.__iter__.return_value = [items]

This way you really mock the iterator returned value.这样您就可以真正模拟迭代器返回的值。

This approach works even when you are mocking complex objects which are iterables and have methods (my case).即使您正在模拟可迭代且具有方法的复杂对象(我的情况),这种方法也有效。

I tried the chosen answer but didtn't work in my case, only worked when I mocked the way I explained我尝试了选择的答案,但在我的情况下不起作用,只有在我嘲笑我解释的方式时才起作用

Wims answer :维姆斯回答

mock_f.return_value = iter(items)

works as long as your mock gets called only once.只要你的模拟只被调用一次就可以工作。 In unit testing, we may often want to call a function or method multiple times with different arguments.在单元测试中,我们可能经常想用不同的参数多次调用一个函数或方法。 That will fail in this case, because on the first call the iterator will be exhausted such that on the second call it will immediately raise a StopIteration exception.在这种情况下这将失败,因为在第一次调用时迭代器将耗尽,因此在第二次调用时它会立即引发StopIteration异常。 With Alexandre Paes' answer I was getting an AttributeError: 'function' object has no attribute '__iter__' when my mock was coming from unittest.mock.patch .通过Alexandre Paes 的回答,当我的模拟来自unittest.mock.patch时,我得到了一个AttributeError: 'function' object has no attribute '__iter__'

As an alternative, we can create a “fake” iterator and assign that as a side_effect :作为替代方案,我们可以创建一个“假”迭代器并将其分配为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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM