Mock有一个有用的assert_called_with()方法。然而,据我所知,这只检查对方法的最后一次调用。 如果我有连续3次调用模拟方法的代码,每次都有不同的参数,我如何使用它们特定的参数断言这3次调用?
当前回答
通常,我不关心电话的顺序,只关心它们发生了什么。在这种情况下,我将assert_any_call与关于call_count的断言结合起来。
>>> import mock
>>> m = mock.Mock()
>>> m(1)
<Mock name='mock()' id='37578160'>
>>> m(2)
<Mock name='mock()' id='37578160'>
>>> m(3)
<Mock name='mock()' id='37578160'>
>>> m.assert_any_call(1)
>>> m.assert_any_call(2)
>>> m.assert_any_call(3)
>>> assert 3 == m.call_count
>>> m.assert_any_call(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "[python path]\lib\site-packages\mock.py", line 891, in assert_any_call
'%s call not found' % expected_string
AssertionError: mock(4) call not found
我发现这样做比将大量调用传递到单个方法中更容易阅读和理解。
如果您确实关心顺序,或者希望有多个相同的调用,那么assert_has_calls可能更合适。
Edit
Since I posted this answer, I've rethought my approach to testing in general. I think it's worth mentioning that if your test is getting this complicated, you may be testing inappropriately or have a design problem. Mocks are designed for testing inter-object communication in an object oriented design. If your design is not objected oriented (as in more procedural or functional), the mock may be totally inappropriate. You may also have too much going on inside the method, or you might be testing internal details that are best left unmocked. I developed the strategy mentioned in this method when my code was not very object oriented, and I believe I was also testing internal details that would have been best left unmocked.
其他回答
我总是要一次又一次地查阅这个问题,所以这是我的答案。
对同一类的不同对象断言多个方法调用
假设我们有一个重载类(我们想要模拟它):
In [1]: class HeavyDuty(object):
...: def __init__(self):
...: import time
...: time.sleep(2) # <- Spends a lot of time here
...:
...: def do_work(self, arg1, arg2):
...: print("Called with %r and %r" % (arg1, arg2))
...:
下面是一些使用了HeavyDuty类的两个实例的代码:
In [2]: def heavy_work():
...: hd1 = HeavyDuty()
...: hd1.do_work(13, 17)
...: hd2 = HeavyDuty()
...: hd2.do_work(23, 29)
...:
下面是heavy_work函数的测试用例:
In [3]: from unittest.mock import patch, call
...: def test_heavy_work():
...: expected_calls = [call.do_work(13, 17),call.do_work(23, 29)]
...:
...: with patch('__main__.HeavyDuty') as MockHeavyDuty:
...: heavy_work()
...: MockHeavyDuty.return_value.assert_has_calls(expected_calls)
...:
我们正在用MockHeavyDuty模拟HeavyDuty类。要断言来自每个HeavyDuty实例的方法调用,必须引用MockHeavyDuty.return_value。assert_has_calls,而不是MockHeavyDuty.assert_has_calls。此外,在expected_calls的列表中,我们必须指定我们感兴趣的断言调用的方法名。所以我们的清单上都是要拨打的电话。Do_work,而不是简单地调用。
测试用例表明它是成功的:
In [4]: print(test_heavy_work())
None
如果我们修改了heavy_work函数,测试就会失败,并产生一条有用的错误消息:
In [5]: def heavy_work():
...: hd1 = HeavyDuty()
...: hd1.do_work(113, 117) # <- call args are different
...: hd2 = HeavyDuty()
...: hd2.do_work(123, 129) # <- call args are different
...:
In [6]: print(test_heavy_work())
---------------------------------------------------------------------------
(traceback omitted for clarity)
AssertionError: Calls not found.
Expected: [call.do_work(13, 17), call.do_work(23, 29)]
Actual: [call.do_work(113, 117), call.do_work(123, 129)]
断言对一个函数的多次调用
为了与上面的对比,这里有一个例子,展示了如何模拟对一个函数的多次调用:
In [7]: def work_function(arg1, arg2):
...: print("Called with args %r and %r" % (arg1, arg2))
In [8]: from unittest.mock import patch, call
...: def test_work_function():
...: expected_calls = [call(13, 17), call(23, 29)]
...: with patch('__main__.work_function') as mock_work_function:
...: work_function(13, 17)
...: work_function(23, 29)
...: mock_work_function.assert_has_calls(expected_calls)
...:
In [9]: print(test_work_function())
None
有两个主要的区别。第一个是在模拟一个函数时,我们使用call来设置预期的调用,而不是使用call.some_method。第二个是我们在mock_work_function上调用assert_has_calls,而不是在mock_work_function.return_value上调用。
通常,我不关心电话的顺序,只关心它们发生了什么。在这种情况下,我将assert_any_call与关于call_count的断言结合起来。
>>> import mock
>>> m = mock.Mock()
>>> m(1)
<Mock name='mock()' id='37578160'>
>>> m(2)
<Mock name='mock()' id='37578160'>
>>> m(3)
<Mock name='mock()' id='37578160'>
>>> m.assert_any_call(1)
>>> m.assert_any_call(2)
>>> m.assert_any_call(3)
>>> assert 3 == m.call_count
>>> m.assert_any_call(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "[python path]\lib\site-packages\mock.py", line 891, in assert_any_call
'%s call not found' % expected_string
AssertionError: mock(4) call not found
我发现这样做比将大量调用传递到单个方法中更容易阅读和理解。
如果您确实关心顺序,或者希望有多个相同的调用,那么assert_has_calls可能更合适。
Edit
Since I posted this answer, I've rethought my approach to testing in general. I think it's worth mentioning that if your test is getting this complicated, you may be testing inappropriately or have a design problem. Mocks are designed for testing inter-object communication in an object oriented design. If your design is not objected oriented (as in more procedural or functional), the mock may be totally inappropriate. You may also have too much going on inside the method, or you might be testing internal details that are best left unmocked. I developed the strategy mentioned in this method when my code was not very object oriented, and I believe I was also testing internal details that would have been best left unmocked.
Assert_has_calls是解决这个问题的另一种方法。
从文档中可以看出:
assert_has_calls (calls, any_order=False) 断言mock已经完成 使用指定的调用调用。检查mock_calls列表 的电话。 如果any_order为False(默认值),则调用必须是顺序的。 在指定的调用之前或之后可以有额外的调用。 如果any_order为True,则调用可以以任何顺序进行,但必须如此 所有都出现在mock_calls中。
例子:
>>> from unittest.mock import call, Mock
>>> mock = Mock(return_value=None)
>>> mock(1)
>>> mock(2)
>>> mock(3)
>>> mock(4)
>>> calls = [call(2), call(3)]
>>> mock.assert_has_calls(calls)
>>> calls = [call(4), call(2), call(3)]
>>> mock.assert_has_calls(calls, any_order=True)
来源:https://docs.python.org/3/library/unittest.mock.html unittest.mock.Mock.assert_has_calls
你可以使用Mock。Call_args_list属性将参数与以前的方法调用进行比较。还有莫克。Call_count属性应该给予您完全的控制权。
推荐文章
- 如何在Python中进行热编码?
- 如何嵌入HTML到IPython输出?
- 在Python生成器上使用“send”函数的目的是什么?
- 是否可以将已编译的.pyc文件反编译为.py文件?
- Django模型表单对象的自动创建日期
- 在Python中包装长行
- 如何计算两个时间串之间的时间间隔
- 我如何才能找到一个Python函数的参数的数量?
- 您可以使用生成器函数来做什么?
- 将Python诗歌与Docker集成
- 提取和保存视频帧
- 使用请求包时出现SSL InsecurePlatform错误
- 如何检索Pandas数据帧中的列数?
- except:和except的区别:
- 错误:“字典更新序列元素#0的长度为1;2是必需的”