I need to test a function which uses feature toggles to turn some functionality on and off. Say, a function like this:
def func_to_test(hello_value: str):
if toggle_is_active('only_hello_and_hi'):
if hello_value not in ('hello', 'hi'):
return
print(hello_value)
And now I want to test this function for both feature toggle states. For the off toggle I would do something like this:
class InactiveToggleTestCase(unittest.TestCase):
hello_values = ['hello', 'hi', 'bonjour', 'sup']
def test_func(self):
for hello_value in self.hello_values:
with self.subTest(hello_value=hello_value):
func_to_test(hello_value)
# assert print was called with expected values
And for the active state I want to just inherit the class and patch the toggle state:
@patch('toggle_is_active', lambda x: True)
class ActiveToggleTestCase(InactiveToggleTestCase):
hello_values = ['hello', 'hi']
This way I don't need to rewrite the test itself. The thing is, the parent class also gets the patch from the child class and test case for inactive toggle state doesn't pass anymore.
How can I avoid this effect? I could just duplicate the test, of course, but this doesn't seem to be right and also if there is a plenty of tests the inheritance would be really helpful.
The thing is, the parent class also gets the patch from the child class and test case for inactive toggle state doesn't pass anymore.
Firstly, I can't reproduce this problem. If I make two files:
t.py
def toggle_is_active(_):
return False
mre.py
import unittest
from unittest.mock import patch
import t
class InactiveToggleTestCase(unittest.TestCase):
def test_func(self):
print(t.toggle_is_active)
@patch('t.toggle_is_active', lambda x: True)
class ActiveToggleTestCase(InactiveToggleTestCase):
pass
unittest.main()
Then running python mre.py
prints
python mre.py
<function <lambda> at 0x109f5b040>
.<function toggle_is_active at 0x10a1b5430>
This shows that the function is being patched for one class and not the other. Could you give the exact steps to reproduce (e.g. the way you call unittest
and the file/folder structure you are using)?
IMO, the real issue is that this is not a good use of inheritance. ActiveToggleTestCase shouldn't really be a child of Inactive and it's not worth shoehorning it into that role just to reduce duplication. I think what you're trying to express is that there is some concept of a ToggleCaseTest and there are two variants of it so you could do
class ToggleMixin():
def test_func(self):
print(t.toggle_is_active)
print(self.hello_values)
# your previous testing code here...
class InactiveToggleTestCase(unittest.TestCase, ToggleMixin):
hello_values = ['hello', 'hi', 'bonjour', 'sup']
@patch('t.toggle_is_active', lambda x: True)
class ActiveToggleTestCase(unittest.TestCase, ToggleMixin):
hello_values = ['hello', 'hi']
If you can't get that to work, you could also do
class ToggleTestCase(unittest.TestCase):
def helper_func(self, hello_values):
for hello_value in hello_values:
with self.subTest(hello_value=hello_value):
func_to_test(hello_value)
# assert print was called with expected values
def test_inactive(self):
hello_values = ['hello', 'hi', 'bonjour', 'sup']
self.helper_func(hello_values)
def test_active(self):
hello_values = ['hello', 'hi']
with patch("t.toggle_is_active") as mock_toggle:
self.helper_func(hello_values)
This should be safer since we are being explicit about when we patch toggle_is_active
.