Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to patch classmethod with autospec in unmocked class?

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?

like image 921
scanny Avatar asked Aug 30 '14 23:08

scanny


2 Answers

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.

like image 152
Felipe Avatar answered Sep 22 '22 15:09

Felipe


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.

like image 20
AManOfScience Avatar answered Sep 26 '22 15:09

AManOfScience