python mock - patching a method without obstructing implementation
PythonMockingPython Problem Overview
Is there a clean way to patch an object so that you get the assert_call*
helpers in your test case, without actually removing the action?
For example, how can I modify the @patch
line to get the following test passing:
from unittest import TestCase
from mock import patch
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
@patch.object(Potato, 'foo')
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
I could probably hack this together using side_effect
, but I was hoping there would be a nicer way which works the same way on all of functions, classmethods, staticmethods, unbound methods, etc.
Python Solutions
Solution 1 - Python
Similar solution with yours, but using wraps
:
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.foo(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
According to the documentation:
> wraps: Item for the mock object to wrap. If wraps is not None then > calling the Mock will pass the call through to the wrapped object > (returning the real result). Attribute access on the mock will return > a Mock object that wraps the corresponding attribute of the wrapped > object (so attempting to access an attribute that doesn’t exist will > raise an AttributeError).
class Potato(object):
def spam(self, n):
return self.foo(n=n)
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(TestCase):
def test_something(self):
spud = Potato()
with patch.object(Potato, 'foo', wraps=spud.foo) as mock:
forty_two = spud.spam(n=40)
mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
Solution 2 - Python
This answer address the additional requirement mentioned in the bounty from user Quuxplusone:
> The important thing for my use-case is that it work with @patch.mock
, i.e. that it not require me to insert any code in between my constructing of the instance of Potato
(spud
in this example) and my calling of spud.foo
. I need spud
to be created with a mocked-out foo
method from the get-go, because I do not control the place where spud
is created.
The use case described above could be achieved without too much trouble by using a decorator:
import unittest
import unittest.mock # Python 3
def spy_decorator(method_to_decorate):
mock = unittest.mock.MagicMock()
def wrapper(self, *args, **kwargs):
mock(*args, **kwargs)
return method_to_decorate(self, *args, **kwargs)
wrapper.mock = mock
return wrapper
def spam(n=42):
spud = Potato()
return spud.foo(n=n)
class Potato(object):
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(unittest.TestCase):
def test_something(self):
foo = spy_decorator(Potato.foo)
with unittest.mock.patch.object(Potato, 'foo', foo):
forty_two = spam(n=40)
foo.mock.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
if __name__ == '__main__':
unittest.main()
If the method replaced accepts mutable arguments which are modified under test, you might wish to initialize a CopyingMock
* in place of the MagicMock
inside the spy_decorator.
*It's a recipe taken from the docs which I've published on PyPI as copyingmock lib
Solution 3 - Python
The OP makes this request:
> I could probably hack this together using side_effect
For those who don't mind using side_effect
, here's a solution with a few pros:
- Uses decorator syntax
- Patches an unbound method, which I find is more versatile
- Requires inclusion of the instance in the assertion
class PotatoTest(TestCase):
@patch.object(Potato, 'foo', side_effect=Potato.foo, autospec=True)
def test_something(self, mock):
spud = Potato()
forty_two = spud.foo(n=40)
mock.assert_called_once_with(spud, n=40)
self.assertEqual(forty_two, 42)