2017-10-20 160 views
3

我有一个递归函数,我正在测试,但是我在测试期间难以限制递归调用。例如,下面是递归函数的一个简单示例,它调用bool_function(n)来检查它是否应该中断递归循环。测试递归Python函数

def factorial(n): 
    if bool_function(n): 
     return 1 
    else: 
     return n * factorial(n-1) 

什么将是检验的最好方式或模拟bool_function(N),所以这是之后的任何呼叫对于第一次迭代真假?

+0

你使用unittest吗? –

+0

除非'bool_function'有副作用,为什么要麻烦。你不能只用'n'来测试你知道命中了'真'吗? – Sylwester

回答

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 
0

我通常尽量不要离开调试代码,除非我期望定期使用它,但为了调试强制执行遵循特定路径,您可以只包含一个默认参数。

def factorial(n, debug=False): 
    if bool_function(n) or debug: 
     return 1 
    else: 
     return n * factorial(n-1) 

这自然意味着你还可以通过外部测试bool_function()

+0

我强烈建议不要传递任何'debug'参数。如果函数不能被测试 - 这意味着设计是不好的,增加一个解决方法并不能保证函数本身的工作。 –

+0

@TarasMatsyk也许这是一个过于简化的例子的结果,但我相信在这种情况下更大的简单性和可读性超过了模块性。特别是如果谓词否则只能是单个函数,这种方法既简化了代码的读取,又简化了执行。 – Aaron

+0

同意从这个角度来看 –

0

只要传递函数作为参数。如果函数是None,那么如果需要,可以应用一些默认行为。

这是queries to iterables(例如Django查询或Peewee查询)在大多数语言中使用的常用方法。

,返回boolean值的函数通常被称为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) 
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 & bool_function=...只是使用somemodule.bool_function=...

如果你想使用模拟库,那么它取决于你使用的库。如果是unittest.mock,那么你应该使用side_effect=... & wraps=...(请参阅manual)。同样的方法:嘲笑它,并从第一次使用时的副作用中取消它。