测试递归Python函数

时间:2017-10-20 19:01:13

标签: python testing recursion mocking

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

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

测试或模拟bool_function(n)的最佳方法是什么,以便第一次迭代时为true,之后的任何调用为false?

5 个答案:

答案 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)