【发布时间】:2021-09-27 21:04:46
【问题描述】:
由于MagicMock 实例的所有成员也是MagicMock 实例,我想我可以只模拟顶级对象,而不必模拟调用链中的每个成员。然后我可以简单地询问我的原始模拟是否以某种方式调用了它的任何成员,例如assert_called。当我尝试这个时,调用列表确实看到了对孩子的调用,但是用于测试它们的断言方法是什么?
例如:
from unittest.mock import MagicMock
# Mock an object
foo = MagicMock()
# Try calling a child
foo.bar().baz()
# foo clearly registers the calls to its child...
print(foo.mock_calls)
# ...but how do I assert them?
foo.bar.assert_called()
foo.bar.baz.assert_called()
这不起作用:
$ python assert_member_calls.py
[call.bar(), call.bar().baz()]
[call.bar(), call.bar().baz()]
Traceback (most recent call last):
File "/(...)/assert_member_calls.py", line 14, in <module>
foo.bar.baz.assert_called()
File "/(...)/python3.9/unittest/mock.py", line 876, in assert_called
raise AssertionError(msg)
AssertionError: Expected 'baz' to have been called.
我知道一种解决方案是基本上为每个属性创建一个模拟,因此除了foo,还要显式模拟bar 和baz 以及它们的返回值。但这似乎是不必要的乏味。如果foo 是模拟,那么bar、bar()、baz 和baz() 也已经自动模拟。我不应该恢复一个新的模拟和补丁foo.bar.baz(这是一个模拟开始),这样我就可以处理mock_baz.assert_called()。为了支持我的想法,显然foo 的调用列表包含call.bar().baz() -- 没有可以做到assert that call.bar().baz() was called 的方法吗?
【问题讨论】:
-
与问题本身无关,但必须访问
foo.bar().baz()[...]并不理想——也许您可以改进bar接口,或创建foo.get_baz_results方法以使测试更容易。重构代码以使其更易于测试(并在以后理解)是测试帮助您编写更好代码的方法之一。 -
@jfaccioni 绝对!然而,在这种情况下,我实际上是在使用外部库,因此更改接口是不切实际的。
标签: python unit-testing mocking pytest pytest-mock