Home > other >  How to assert member calls?
How to assert member calls?

Time:09-28

Since all members of a MagicMock instance are also MagicMock instances, I figured I could just mock the top-level object without having to mock every single member along a chain of calls. Then I could simply ask my original mock if any of its members have been called in a certain way, like with assert_called. When I try this, the call list does indeed see the calls to children, but what is the assert method for testing them?

For example:

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()

This doesn't work:

$ 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.

I know that one solution is to basically create one mock for every attribute, so in addition to foo, also explicitly mock bar and baz along with their return value. But that seems needlessly tedious. If foo is a mock then bar, bar(), baz and baz() are already automatically mocks as well. I shouldn't have to reinstatiate a new mock and patch foo.bar.baz (which was a mock to begin with) just so I can have a handle to do mock_baz.assert_called(). To support my thinking, clearly the call list for foo contains call.bar().baz() -- is there not a method that can do assert that call.bar().baz() was called?

CodePudding user response:

You can check the propertiel called from every mock object. (Using unittest, as it seems you are using), you could use the following code:

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

CodePudding user response:

It's a simple mistake in your assertions. The first assertion as written is OK. But the second assertion should have been foo.bar().baz.assert_called(), since baz is called on the return value foo.bar(), and not directly on foo.bar itself.

These assertions work:

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

Other way to do the same thing:

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