Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: unbound method when trying to mock a classmethod

This scripts fails:

import mock

class MyClass(object):

    @classmethod
    def my_method(cls):
        print('my_method')

def mocked_method(cls):
    print('I want this method to get called')

with mock.patch.object(MyClass, 'my_method', mocked_method):
    MyClass.my_method()

Exception:

Traceback (most recent call last):
  File "/home/foo/tmp/test_mocking_classmethod.py", line 14, in <module>
    MyClass.my_method()
TypeError: unbound method mocked_method() must be called with MyClass instance as first argument (got nothing instead)
like image 535
guettli Avatar asked Mar 24 '15 14:03

guettli


1 Answers

Python functions are descriptors and Python binds these to the instance they are looked up on, or in the case of classmethod, to the class. Because you didn't use the classmethod decorator on the replacement function, it is being bound wrongly (as a regular method instead, so no cls is being passed in).

Simply wrap the target in a classmethod decorator manually:

with mock.patch.object(MyClass, 'my_method', classmethod(mocked_method)):
    MyClass.my_method()

Here I applied the @classmethod decorator manually, but you could also just use it as intended, as a decorator, directly on the target function:

@classmethod
def mocked_method(cls):
    print('I want this method to get called')

with mock.patch.object(MyClass, 'my_method', mocked_method):
    MyClass.my_method()

Demo:

>>> import mock
>>> class MyClass(object):
...     @classmethod
...     def my_method(cls):
...         print('my_method')
... 
>>> def mocked_method(cls):
...     print('I want this method to get called')
... 
>>> with mock.patch.object(MyClass, 'my_method', classmethod(mocked_method)):
...     MyClass.my_method()
... 
I want this method to get called
like image 171
Martijn Pieters Avatar answered Sep 29 '22 05:09

Martijn Pieters