Unit-tests should test functionality and try to be agnostic to implementation details.
Mock.assert_called_with() is a convenient function, yet AFAIK it compares *args to *args and **kwargs to **kwargs. Therefore:
# class to be mocked during test
class SomeClass():
def func(self,a,b,c=5):
# ...
# code under test
somaclass_instance.func(1,b=2,c=3)
# test code that works
someclass_mock.func.assert_called_with(1,b=2,c=3)
# test code that won't work
someclass_mock.func.assert_called_with(1,2,c=3)
someclass_mock.func.assert_called_with(a=1,b=2,c=3)
is there a way to generalize this so that the specifics of which *args where used as **kwargs in the call to func, which is really an implementation detail, will be ignored?
Since Python 3.4, just like you wanted, asserting for specific call signatures is normalized automatically when a callable Mock is created with a spec, and for object methods when using auto-speccing.
From the very end of the documentation of the Mock class:
A callable mock which was created with a spec (or a spec_set) will introspect the specification object’s signature when matching calls to the mock. Therefore, it can match the actual call’s arguments regardless of whether they were passed positionally or by name:
>>> def f(a, b, c): pass ... >>> mock = Mock(spec=f) >>> mock(1, 2, c=3) <Mock name='mock()' id='140161580456576'> >>> mock.assert_called_with(1, 2, 3) >>> mock.assert_called_with(a=1, b=2, c=3)This applies to assert_called_with(), assert_called_once_with(), assert_has_calls() and assert_any_call(). When Autospeccing, it will also apply to method calls on the mock object.
Changed in version 3.4: Added signature introspection on specced and autospecced mock objects.
File a feature request to mock.
Fundamental problem is that without access to real function/class mock has no way of knowing the order of keyword arguments, that is invocations call(a=1, b=2) and call(b=2, a=1) look identical to mock, while invocations call(1, 2) and call(2, 1) do not.
If you wish to generalize mock, you will need to pass a call prototype or a function in lieu of prototype, e.g:
amock.afunc.assert_called_with(1, 2, c=3, __prototype__=lambda a=None, b=None, c=None: None)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With