I have a list of functions that return Boolean values. It's all called like this:
for f in functions:
if f():
pass
I am writing tests and I want to mock the result of those functions
@pytest.fixture
def is_first_func_mock(mocker):
mock = mocker.patch.object(SomeClass, 'first_func')
return mock
and then somewhere in the test I pass this fixture and write
is_first_func_mock.return_value = True
or something like that
But since I'm not calling the function directly in the code, and since I indicated it above, the mock does not work.
I found a solution in Stackoverflow: I can call functions like that:
if getattr(funcs_module, func.__name__)():
pass
But I don't like it and I don't want to change the loop
Update:
def some_name(fucntions: list[callable]):
def inner(func):
@wraps(func)
async def wrapper(*args, **kwargs):
#some code
for f in functions:
if f():
return await func(*args, **kwargs)
raise SomePermission
return wrapper
return inner
I haven't been able to find a solution, but here is a workaround:
from functools import wraps
def has_any_permissions(get_functions):
def inner(func):
@wraps(func)
async def wrapper(*args, **kwargs):
# some code
funcs = get_functions()
if any(f() for f in funcs):
return await func(*args, **kwargs)
raise SomePermission()
return wrapper
return inner
def is_admin():
return False
def is_users_moderator():
return False
def admin_and_users_moderator():
return [is_admin, is_users_moderator]
@has_any_permissions(admin_and_users_moderator)
def about_user():
return 'user' # just for demonstration purposes
I have had to introduce some indirection where the decorator does not bind the actual list of functions at decoration time, but instead supplies a function which, when called inside wrapper()
gets the functions each time the target could be called.
The corresponding unit test might look like this:
import unittest
from unittest.mock import patch
from mockable_functions import about_user
class Test(unittest.TestCase):
def test_plain_function_raises(self):
self.assertRaises(SomePermission, about_user)
@patch('mockable_functions.is_admin')
def test_func(self, is_admin_mock):
is_admin_mock.return_value = True
self.assertEqual(about_user(), 'user', 'about_user returns user')
This unit test shows that about_user()
raises an error when called normally, but when the is_admin
function is patched, then the real function gets called (and returns a dummy value)