I want to assert that one classmethod in a Python class calls another classmethod with a certain set of arguments. I would like the mocked classmethod to be "spec-ed", so it detects if it is called with the wrong number of arguments.
When I patch the classmethod using patch.object(.., autospec=True, ..)
, the classmethod is replaced with a NonCallableMagicMock
and raises an error when I try to call it.
from mock import patch
class A(object):
@classmethod
def api_meth(cls):
return cls._internal_classmethod(1, 2, 3)
@classmethod
def _internal_classmethod(cls, n, m, o):
return sum(n, m, o)
with patch.object(A, '_internal_classmethod') as p:
print(type(p).__name__)
with patch.object(A, '_internal_classmethod', autospec=True) as p:
print(type(p).__name__)
produces the output:
MagicMock
NonCallableMagicMock
How can I get a spec-ed mock for _internal_classmethod
when the class it belongs to is not mocked?
There's an outstanding bug report (google code link and python bug tracker link) to fix this issue. Until the fix gets incorporated, you can try the following, which worked for me [On 2.7
, though I think it would also work in 3.x
].
def _patched_callable(obj):
"Monkeypatch to allow autospec'ed classmethods and staticmethods."
# See https://code.google.com/p/mock/issues/detail?id=241 and
# http://bugs.python.org/issue23078 for the relevant bugs this
# monkeypatch fixes
if isinstance(obj, type):
return True
if getattr(obj, '__call__', None) is not None:
return True
if (isinstance(obj, (staticmethod, classmethod))
and mock._callable(obj.__func__)):
return True
return False
_patched_callable._old_func = mock._callable
mock._callable = _patched_callable
After the monkeypatch, you should be able to use mock.patch
normally and have static- and class-methods patched properly.
Use spec
in place of autospec
, and set it directly.
with patch.object(A, '_internal_classmethod', spec=A._internal_classmethod) as p:
print(type(p).__name__)
gives me
MagicMock
for output.
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