如何用 mock.patch 模拟生成器
How to mock generators with mock.patch
我浏览了页面 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
更改此行:
mock_f.iter.return_value = items
为此:
mock_f.return_value = iter(items)
我有另一种方法:
mock_f.__iter__.return_value = [items]
这样你就真正模拟了迭代器的返回值。
即使您模拟可迭代且具有方法的复杂对象(我的情况),这种方法也适用。
我尝试了选择的答案,但在我的情况下不起作用,只有当我嘲笑我解释的方式时才起作用
:
mock_f.return_value = iter(items)
只要你的模拟只被调用一次就可以工作。在单元测试中,我们可能经常想用不同的参数多次调用一个函数或方法。在这种情况下,这将失败,因为在第一次调用时迭代器将耗尽,因此在第二次调用时它将立即引发 StopIteration
异常。 当我的模拟来自 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
我浏览了页面 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
更改此行:
mock_f.iter.return_value = items
为此:
mock_f.return_value = iter(items)
我有另一种方法:
mock_f.__iter__.return_value = [items]
这样你就真正模拟了迭代器的返回值。
即使您模拟可迭代且具有方法的复杂对象(我的情况),这种方法也适用。
我尝试了选择的答案,但在我的情况下不起作用,只有当我嘲笑我解释的方式时才起作用
mock_f.return_value = iter(items)
只要你的模拟只被调用一次就可以工作。在单元测试中,我们可能经常想用不同的参数多次调用一个函数或方法。在这种情况下,这将失败,因为在第一次调用时迭代器将耗尽,因此在第二次调用时它将立即引发 StopIteration
异常。 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