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().

Asked By: mojo1mojo2

||

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.

Answered By: progmatico

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()
Answered By: pymen

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!
Answered By: ArtOfWarfare
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.