测试递归Python函数

测试递归Python函数,python,testing,recursion,mocking,Python,Testing,Recursion,Mocking,我有一个递归函数要测试,但是在测试期间我很难限制递归调用。例如,下面是一个简单的递归函数示例,它调用bool_函数(n)来检查它是否应该中断递归循环 def factorial(n): if bool_function(n): return 1 else: return n * factorial(n-1) 测试或模拟bool_函数(n)的最佳方法是什么,以使其在第一次迭代中为真,在之后的任何调用中为假 除非我希望定期使用调试代码,否则我通常会尽量不留下调试代码

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

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

测试或模拟bool_函数(n)的最佳方法是什么,以使其在第一次迭代中为真,在之后的任何调用中为假

除非我希望定期使用调试代码,否则我通常会尽量不留下调试代码,但为了调试,您可以只包含一个默认参数,以强制执行遵循特定路径

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

这自然意味着您也在外部测试
bool_function()

只需将函数作为参数传递即可。如果函数为“无”,则可以应用一些默认行为(如果需要)

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

返回布尔值的函数通常称为


您可以始终实现一个类来封装状态,并为您提供更大的灵活性,下面是一个示意图:

>>> 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
>>类MockBoolCheck:
...     def uuu init uuu(self,fail_after=0):
...         self.count=0
...         self.fail\u after=失败后
...     定义调用(self,n):
...         调用=self.count
...         self.count+=1
...         返回调用>>bool_函数=MockBoolCheck()
>>>布尔函数(42)
真的
>>>布尔函数(42)
假的
>>>布尔函数(42)
假的
>>>布尔函数(42)
假的
>>>布尔函数(42)
假的

如果除了其他建议的解决方案之外,您真的想要模拟它,并且想要自己(不需要模拟库)通过替换模拟函数来完成

# 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)
#您的代码(或模块):
def bool_功能(n):
打印('REAL bool函数{}'。格式(n))
对于python>3.6,返回n

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)

你在使用unittest吗?除非
bool\u函数
有副作用,否则何必麻烦呢。你不能用
n
测试你知道的
True
?我强烈建议不要传递任何
debug
参数。如果函数无法测试-这意味着设计不好,再增加一个变通方法并不能保证函数本身可以工作。@TarasMatsyk也许这是一个过于简化的例子的结果,但我相信在这种情况下,更大的简单性和可读性超过了模块性。特别是如果谓词仅是一个函数,这种方法既简化了代码的读取,也简化了执行
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)