I want to mock an instance method. First I want to modify the arguments and then I want to call the original method.
I tried this:
import mock
class Foo(object):
def my_method(data):
print(data)
def wrapped_method(data):
return Foo.my_method(data.replace('x', 'o'))
with mock.patch.object(Foo, 'my_method', wraps=wrapped_method):
foo = Foo()
foo.my_method('axe') # should print "aoe"
But I get this exception:
/home/foo/bin/python /home/foo/src/wrap-instance-method.py
Traceback (most recent call last):
File "/home/foo/src/wrap-instance-method.py", line 15, in <module>
foo.my_method('axe') # should print "aoe"
File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 955, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 1024, in _mock_call
return self._mock_wraps(*args, **kwargs)
.....
return self._mock_wraps(*args, **kwargs)
File "/home/foo/src/wrap-instance-method.py", line 10, in wrapped_method
return Foo.my_method(data.replace('x', 'o'))
File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 955, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/home/foo/local/lib/python2.7/site-packages/mock.py", line 960, in _mock_call
self.called = True
RuntimeError: maximum recursion depth exceeded while calling a Python object
Process finished with exit code 1
How can I call the original method which was wrapped without recursion exception?
You replaced the Foo.my_method with a method that calls Foo.my_method, so yes, you'll get an infinite recursion.
If you must have the original method, you need to store it before you patch it:
def mock_method_factory(original):
def wrapped_method(data):
return original(data.replace('x', 'o'))
return wrapped_method
with mock.patch.object(Foo, 'my_method', wraps=mock_method_factory(Foo.my_method)):
# ...
However, wraps does not handle binding; original is an unbound function and wrapped_method won't be passed in self.
The mock library is perhaps not the best choice here. What you are essentially doing is apply a temporary decorator. Either subclass Foo or apply the decorator manually to Foo.my_method:
def mock_method_factory(original):
def wrapped_method(self, data):
return original(self, data.replace('x', 'o'))
return wrapped_method
class MockedFoo(Foo):
my_method = mock_method_factory(Foo.my_method)
foo = MockedFoo()
foo.my_method('axe') # should print "aoe"
or
def mock_method_factory(original):
def wrapped_method(self, data):
return original(self, data.replace('x', 'o'))
return wrapped_method
original = Foo.my_method
try:
Foo.my_method = mock_method_factory(original)
foo = Foo()
foo.my_method('axe') # should print "aoe"
finally:
Foo.my_method = original
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