如何断言成员调用?

How to assert member calls?

因为 MagicMock 实例的所有成员也是 MagicMock 实例,我想我可以模拟 top-level object 而不必模拟每个成员一连串的电话。然后我可以简单地询问我的原始模拟是否以某种方式调用了它的任何成员,比如 assert_called。当我尝试这个时,调用列表确实看到了对 children 的调用,但是测试它们的断言方法是什么?

例如:

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 之外,还显式模拟 barbaz 以及它们的 return 值。但这似乎不必要地乏味。如果 foo 是 mock,那么 barbar()bazbaz() 也已经自动成为 mock。我不应该恢复一个新的模拟和补丁 foo.bar.baz(这是一个模拟开始)只是为了我可以有一个句柄来做 mock_baz.assert_called()。为了支持我的想法,foo 的调用列表显然包含 call.bar().baz()——难道没有一种方法可以做到 assert that call.bar().baz() was called

您可以检查每个模拟对象的属性 called。 (使用 unittest,看起来你正在使用),你可以使用以下代码:

# ...but how do I assert them?
def test_called(self):
    self.assertTrue(foo.bar.called)
    self.assertTrue(foo.bar().baz.called)

这是您的断言中的一个简单错误。所写的第一个断言是可以的。但是第二个断言应该是 foo.bar().baz.assert_called(),因为 baz 是在 return 值 foo.bar() 上调用的,而不是直接在 foo.bar 上调用的本身。

这些断言有效:

foo.bar.assert_called()
foo.bar().baz.assert_called()

做同样事情的其他方法:

foo.bar.assert_called()
foo.bar.return_value.baz.assert_called()