Python Patch/Mock class method but still call original method
Question:
I want to use patch to record all function calls made to a function in a class for a unittest, but need the original function to still run as expected. I created a dummy code example below:
from mock import patch
class A(object):
def __init__(self):
self._a = 1
class B(A):
def __init__(self):
super(B, self).__init__() # TypeError: super() argument 1 must be type, not MagicMock
self._b = 11
def bar(self, b):
self._b = self._b + 1 + b
def foo(self, b):
self.bar(b)
class MockB(B):
def foo(self, b):
super(MockB, self).foo(self, b)
@patch('main.B')
def main(b_class):
b_class.side_effect = MockB
b = B()
print b._b # 11
b.foo(0)
print b._b # 12
main()
In my case, the instance of the class b = B()
is not actually in the main function but in another module, so I can’t Mock the instance. I need it to generically be a decorator for all instances of B.
Summary: I am not sure how to individually mock the class method on it’s own, but still call the original method. After, I want to use something like call_args_list where I can see all calls made to foo()
.
Answers:
I think you are looking for the wraps
Mock parameter. Search the official documentation for wraps
. Accessing attributes returns a mock object. Calling methods gives the real method result instead, if a return value is not configured for the mock.
More details can be found here: https://stackoverflow.com/a/61963740/1731460
import mock
from contextlib import contextmanager
@contextmanager
def mock_patch_method_original(mock_path, original_method, results):
results = iter(results)
def side_effect(self, *args, **kwargs):
value = next(results)
if value == '__original__':
side_effect.self = self
return original_method(self, *args, **kwargs)
else:
return value
patcher = mock.patch(mock_path, autospec=True, side_effect=side_effect)
yield patcher.start()
patcher.stop()
Just keep a reference to your original, non-mocked/patched instance around.
I had an issue that I was patching ctypes.windll.winmm.mciSendStringW
, but I still wanted to be able to access that original function. Initially, I had tried:
from ctypes import windll
originalWindll = windll
# Later on trying to call the original mciSendStringW when it's patched...
originalWindll.winmm.mciSendStringW(...) # Didn't work! Called the patched version of the function!
The correct thing to do was actually this… since it was the function being mocked/patched, it was the function that I needed to keep a reference to.
from ctypes import windll
originalMCISendStringW = windll.winmm.mciSendStringW
# Later on trying to call the original mciSendStringW when it's patched...
originalMCISendStringW(...) # Works! Calls the original, non-patched version of this function!
I want to use patch to record all function calls made to a function in a class for a unittest, but need the original function to still run as expected. I created a dummy code example below:
from mock import patch
class A(object):
def __init__(self):
self._a = 1
class B(A):
def __init__(self):
super(B, self).__init__() # TypeError: super() argument 1 must be type, not MagicMock
self._b = 11
def bar(self, b):
self._b = self._b + 1 + b
def foo(self, b):
self.bar(b)
class MockB(B):
def foo(self, b):
super(MockB, self).foo(self, b)
@patch('main.B')
def main(b_class):
b_class.side_effect = MockB
b = B()
print b._b # 11
b.foo(0)
print b._b # 12
main()
In my case, the instance of the class b = B()
is not actually in the main function but in another module, so I can’t Mock the instance. I need it to generically be a decorator for all instances of B.
Summary: I am not sure how to individually mock the class method on it’s own, but still call the original method. After, I want to use something like call_args_list where I can see all calls made to foo()
.
I think you are looking for the wraps
Mock parameter. Search the official documentation for wraps
. Accessing attributes returns a mock object. Calling methods gives the real method result instead, if a return value is not configured for the mock.
More details can be found here: https://stackoverflow.com/a/61963740/1731460
import mock
from contextlib import contextmanager
@contextmanager
def mock_patch_method_original(mock_path, original_method, results):
results = iter(results)
def side_effect(self, *args, **kwargs):
value = next(results)
if value == '__original__':
side_effect.self = self
return original_method(self, *args, **kwargs)
else:
return value
patcher = mock.patch(mock_path, autospec=True, side_effect=side_effect)
yield patcher.start()
patcher.stop()
Just keep a reference to your original, non-mocked/patched instance around.
I had an issue that I was patching ctypes.windll.winmm.mciSendStringW
, but I still wanted to be able to access that original function. Initially, I had tried:
from ctypes import windll
originalWindll = windll
# Later on trying to call the original mciSendStringW when it's patched...
originalWindll.winmm.mciSendStringW(...) # Didn't work! Called the patched version of the function!
The correct thing to do was actually this… since it was the function being mocked/patched, it was the function that I needed to keep a reference to.
from ctypes import windll
originalMCISendStringW = windll.winmm.mciSendStringW
# Later on trying to call the original mciSendStringW when it's patched...
originalMCISendStringW(...) # Works! Calls the original, non-patched version of this function!