2017-10-20 122 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)。同樣的方法:嘲笑它,並從第一次使用時的副作用中取消它。