Reputation: 637
I have a recursive function that I'm looking to test, however I'm having difficulty limiting the recursive call during testing. For example, below is a simple example of a recursive function that calls a bool_function(n) to check if it should break the recursive loop.
def factorial(n):
if bool_function(n):
return 1
else:
return n * factorial(n-1)
What would be the best way to test or mock bool_function(n) so that it is true for the first iteration and false for any call after?
Upvotes: 3
Views: 5134
Reputation: 2011
For python > 3.6
import mock
class RecursividadeTest(unittest.TestCase):
def test_recursive(self):
with mock.patch('path.factorial') as mock_fact:
factorial(3)
self.assertTrue(mock_fact.called)
self.assertGreaterEqual(mock_fact.call_count, 2)
def test_recursive_2(self):
with mock.patch('incolumepy.sequences.fibonacci.fibonacci') as mock_fib:
for i in range(1, 5, -1):
expected = i - 1
fibonacci(i)
self.assertTrue(mock_fib.called)
self.assertEqual(mock_fib.call_count, expected)
Upvotes: 0
Reputation: 4189
If, beside other suggested solutions, you really want to mock it, and want to do it yourself (without the mocking libraries) by just replacing the mocked function.
# Your code (or module):
def bool_function(n):
print('REAL bool-function {}'.format(n))
return n <= 0
def factorial(n):
print('FACT {}'.format(n))
if bool_function(n):
return 1
else:
return n * factorial(n-1)
# Mocking code (or module):
def mock_function(n):
print('MOCK bool-function {}'.format(n))
global bool_function
bool_function = bool_func_orig # restore on the first use
return False
bool_func_orig = bool_function
bool_function = mock_function # mock it
# Go run it!
factorial(10)
If these are two separate modules, then instead of global bool_function
& bool_function=...
just use the somemodule.bool_function=...
.
If you want to use the mocking library, then it depends on which library you use. If that is unittest.mock
, then you should play with side_effect=...
& wraps=...
(see the manual). The same approach: mock it, and un-mock it from inside the side effect on the first use.
Upvotes: 1
Reputation: 96287
You could always implement a class to encapsulate the state and give you more flexibility, here's a sketch:
>>> class MockBoolCheck:
... def __init__(self, fail_after=0):
... self.count = 0
... self.fail_after = fail_after
... def __call__(self, n):
... called = self.count
... self.count += 1
... return called <= self.fail_after
...
>>> bool_function = MockBoolCheck()
>>> bool_function(42)
True
>>> bool_function(42)
False
>>> bool_function(42)
False
>>> bool_function(42)
False
>>> bool_function(42)
False
Upvotes: 5
Reputation: 3705
Just pass the function as an argument. If function is None you can apply some default behavior if that is desired.
This is a common approach used in queries to iterables
(e.g. Django queries or Peewee queries) in most of languages.
A function that returns boolean is usually called a predicate
def factorial(n, predicate=None):
if not predicate:
predicate = lambda x: x > 2
if predicate(n):
return 1
else:
return n * factorial(n-1)
Upvotes: 0
Reputation: 11075
I generally try not to leave debug code around unless I expect to use it regularly, but you could just include a default argument for the sake of debugging to force the execution to follow a particular path.
def factorial(n, debug=False):
if bool_function(n) or debug:
return 1
else:
return n * factorial(n-1)
This naturally implies that you're also externally testing bool_function()
Upvotes: 0