Reputation: 145
Here's a toy example that illustrates my problem.
class Bar:
def do_a_thing(self):
print('doing a thing')
class BarSupplier:
def get_bar(self) -> Bar:
return Bar()
class Foo:
def __init__(self, bar_supplier: BarSupplier):
self.bar_supplier = bar_supplier
def do_foo(self):
self.bar_supplier.get_bar().do_a_thing()
from unittest import TestCase
from unittest.mock import MagicMock, call
from calls_example import Foo
class TestCallsExample(TestCase):
def test_once(self):
bar_supplier = MagicMock()
bar_supplier.get_bar.return_value = MagicMock()
foo = Foo(bar_supplier)
foo.do_foo()
bar_supplier.get_bar.assert_has_calls([
call(),
])
def test_twice(self):
bar_supplier = MagicMock()
bar_supplier.get_bar.return_value = MagicMock()
foo = Foo(bar_supplier)
foo.do_foo()
foo.do_foo()
bar_supplier.get_bar.assert_has_calls([
call(),
call()
])
The first test passes.
The second test fails, with the following exception:
Failure
Traceback (most recent call last):
...
AssertionError: Calls not found.
Expected: [call(), call()]
Actual: [call(), call().do_a_thing(), call(), call().do_a_thing()]
This feels like really strange behaviour - I'm asserting about calls to the get_bar
method on the bar_supplier
mock, but then the calls list includes calls to a different mock which is returned by the get_bar
method.
I'm sure this is a misunderstanding rather than a bug, but how can I best avoid getting those .do_a_thing()
calls in my list of calls?
Upvotes: 4
Views: 2975
Reputation: 10709
It is because the same mock for .get_bar()
always subsequently calls .do_a_thing()
which as documented:
assert_has_calls(calls, any_order=False)
assert the mock has been called with the specified calls. The
mock_calls
list is checked for the calls.
Wherein mock_calls includes not just the calls to itself:
mock_calls
mock_calls records all calls to the mock object, its methods, magic methods and return value mocks.
You can use the any_order=True
setting for assert_has_calls which as documented:
If
any_order
is false then the calls must be sequential. There can be extra calls before or after the specified calls.If
any_order
is true then the calls can be in any order, but they must all appear in mock_calls.
So change:
bar_supplier.get_bar.assert_has_calls([
call(),
call()
])
To:
bar_supplier.get_bar.assert_has_calls([
call(),
call()
],
any_order=True)
An alternative is checking call_args_list instead:
assert bar_supplier.get_bar.call_args_list == [
call(),
call()
]
Upvotes: 3