我有一个我想要测试的递归函数,但是我在测试期间难以限制递归调用。例如,下面是递归函数的一个简单示例,它调用bool_function(n)来检查它是否应该破坏递归循环。
def factorial(n):
if bool_function(n):
return 1
else:
return n * factorial(n-1)
测试或模拟bool_function(n)的最佳方法是什么,以便第一次迭代时为true,之后的任何调用为false?
答案 0 :(得分:3)
你总是可以实现一个类来封装状态并给你更多的灵活性,这里有一个草图:
>>> 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
答案 1 :(得分:1)
如果,除了其他建议的解决方案之外,你真的想要嘲笑它,并且想要通过替换模拟函数来自己做(没有模拟库)。
# 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)
如果这是两个单独的模块,那么而不是global bool_function
&amp; bool_function=...
只需使用somemodule.bool_function=...
。
如果要使用模拟库,则取决于您使用的库。如果是unittest.mock
,那么您应该使用side_effect=...
&amp; wraps=...
(请参阅manual)。同样的方法:嘲笑它,并在第一次使用时从副作用中解开它。
答案 2 :(得分:0)
我通常尽量不留下调试代码,除非我希望定期使用它,但你可以为了调试而包含一个默认参数,以强制执行遵循特定的路径。
def factorial(n, debug=False):
if bool_function(n) or debug:
return 1
else:
return n * factorial(n-1)
这自然意味着您还在外部测试bool_function()
答案 3 :(得分:0)
只需将该函数作为参数传递。如果function为None,则可以根据需要应用某些默认行为。
这是大多数语言中queries to iterables
(例如Django查询或Peewee查询)中使用的常用方法。
返回布尔值的函数通常称为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)
答案 4 :(得分:0)
对于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)