In the end I used Python's mock, which allows me to override complicated_function without having to adjust the original code in any way.
Here is the original script, and note that complicated_function is not passed in to my_loopy_function as an 'action_function' parameter (which was what I tried in my earlier solutions):
// my_script.py
def my_loopy_function():
aggregate_value = 0
for x in range(10):
aggregate_value = aggregate_value + complicated_function(x)
return aggregate_value
def complicated_function(x):
a = do()
b = something()
c = complicated()
return a + b + c
and here is the script I am using to test it:
// test_my_script.py
import unittest
import mock
from my_script import my_loopy_function
class TestMyModule(unittest.TestCase):
@mock.patch('my_script.complicated_function')
def test_1(self, mocked):
mocked.return_value = 1
self.assertEqual(10, my_loopy_function())
This works just as I had wanted:
- I am able to substitute functions with a simpler version of themselves that I can more easily test,
- I do not need to alter my original code in any way (such as I was trying -- which was effectively by passing in function pointers), the
mockmodule gives me post-coding access to the innards.
Thanks to austin for his suggestion to use mock.
BTW I am using Python 2.7 and therefore used the pip-installable mock from PyPI.
In the end I used Python's mock, which allows me to override complicated_function without having to adjust the original code in any way.
Here is the original script, and note that complicated_function is not passed in to my_loopy_function as an 'action_function' parameter (which was what I tried in my earlier solutions):
// my_script.py
def my_loopy_function():
aggregate_value = 0
for x in range(10):
aggregate_value = aggregate_value + complicated_function(x)
return aggregate_value
def complicated_function(x):
a = do()
b = something()
c = complicated()
return a + b + c
and here is the script I am using to test it:
// test_my_script.py
import unittest
import mock
from my_script import my_loopy_function
class TestMyModule(unittest.TestCase):
@mock.patch('my_script.complicated_function')
def test_1(self, mocked):
mocked.return_value = 1
self.assertEqual(10, my_loopy_function())
This works just as I had wanted:
- I am able to substitute functions with a simpler version of themselves that I can more easily test,
- I do not need to alter my original code in any way (such as I was trying -- which was effectively by passing in function pointers), the
mockmodule gives me post-coding access to the innards.
Thanks to austin for his suggestion to use mock.
BTW I am using Python 2.7 and therefore used the pip-installable mock from PyPI.
Don't try to override complicated_function with action_function, just use complicated_function as the default action_function:
def my_loopy_function(action_function=complicated_function):
aggregate_value = 0
for x in range(10):
aggregate_value = aggregate_value + action_function(x)
return aggregate_value
python - How to override a mock for an individual test within a class that already has a mock - Stack Overflow
mocking - Python: using Mock to override class method - Stack Overflow
python - How can I mock external function that is used in class method? - Stack Overflow
The function is not overwritten when using monkeypatch or mocker (pytest-mock)
Yes! You can leverage the setUp/tearDown methods of the unittest.TestCase and the fact that unittest.mock.patch in its "pure" form (i.e. not as context manager or decorator) returns a "patcher" object that has start/stop methods to control when exactly it should do its magic.
You can call on the patcher to start inside setUp and to stop inside tearDown and if you keep a reference to it in an attribute of your test case, you can easily stop it manually in selected test methods. Here is a full working example:
from unittest import TestCase
from unittest.mock import patch
class Foo:
@staticmethod
def bar() -> int:
return 1
class TestMyThing(TestCase):
def setUp(self) -> None:
self.foo_bar_patcher = patch.object(Foo, "bar", return_value=42)
self.mock_foo_bar = self.foo_bar_patcher.start()
super().setUp()
def tearDown(self) -> None:
self.foo_bar_patcher.stop()
super().tearDown()
def test_a(self):
self.assertEqual(42, Foo.bar())
def test_b(self):
self.assertEqual(42, Foo.bar())
def test_c(self):
self.assertEqual(42, Foo.bar())
def test_d(self):
self.foo_bar_patcher.stop()
self.assertEqual(1, Foo.bar())
This patching behavior is the same, regardless of the different variations like patch.object (which I used here), patch.multiple etc.
Note that for this example it is not necessary to keep a reference to the actual MagicMock instance generated by the patcher in an attribute, like I did with mock_foo_bar. I just usually do that because I often want to check something against the mock instance in my test methods.
It is worth mentioning that you can also use setUpClass/tearDownClass for this, but then you need to be careful with re-starting the patch, if you stop it because those methods are (as the name implies) only called once for each test case, whereas setUp/tearDown are called once before/after each test method.
PS: The default implementations of setUp/tearDown on TestCase do nothing, but it is still good practice IMO to make a habit of calling the superclass' method, unless you deliberately want to omit that call.
While accepted answer is perfectly fine, I'd go for approach which keeps decorator for the class and uses context managers for methods in which you want to override a mock, see sample code below:
@mock.patch("foo", bar)
class TestMyThing(TestCase):
def test_A(self):
assert something
def test_B(self):
assert something
def test_C(self):
assert something
def test_D(self):
with patch('foo', baz):
assert something
This achieves your goal, as context manager mock is applied inside of a test function, so it happens after applying the class decorator mock and therefore it effectively overrides class decorator mock. Also, I think it's simpler and it requires less code, especially if you have no other reason to implement setUp() and tearDown() methods.