2008-11-28 228 views
8

我來自Java世界,您可以隱藏變量和函數,然後使用反射對它們運行單元測試。我已經使用嵌套函數來隱藏我的類的實現細節,以便只有公共API可見。我正在嘗試針對這些嵌套函數編寫單元測試,以確保在開發時不會破壞它們。我已經打過電話像嵌套功能之一:嵌套函數運行單元測試

def outer(): 
    def inner(): 
     pass 

outer.inner() 

導致的錯誤信息:

AttributeError: 'function' object has no attribute 'inner'

有我的方式來編寫針對這些嵌套函數單元測試?如果沒有,有沒有辦法通過在__前面加上類似變量來爲類變量觸發名稱調用的函數名稱?

回答

5

Python約定是命名帶有下劃線的「私有」函數和方法。當你看到一個前導下劃線時,你不會嘗試和使用它。

請記住,Python is not Java

+11

這個問題如何回答?是否有很好的解決方案來隔離單元測試的這些內部函數?即使沒有從外部函數對象訪問內部函數,也可能通過使用python模塊解析器,ast或tokenizer來實現代碼本身。顯然這比編碼更容易。 :-) – 2011-07-09 16:02:06

+1

它回答了這個問題,因爲沒有*測試私人的實現細節。你可以依靠測試覆蓋工具來告訴你,如果你測試的公共接口充分行使私有實現。 – SingleNegationElimination 2011-07-09 16:24:50

11

內部不存在,直到外部使它。你應該將內層移動到頂層函數以進行可測試性,或者讓外層測試測試自身和內層的所有可能的執行路徑。

請注意,內部函數不是一個簡單的函數,它是一個閉包。考慮這種情況:

def outer(a): 
    b = compute_something_from(a) 
    def inner(): 
     do_something_with(a, b) 

這是標準的可測試性權衡。如果你的cyclomatic complexity太高,你的測試將會太多。

1

我不認爲有任何機會從extern命名空間訪問inner()。

但是,在我看來,保持inner()嵌套的事實意味着真正重要的唯一「契約」是outer()的一個。 inner()是實現的一部分,你不應該想測試實現。 如果您真的想測試inner(),請使用包含inner()的所有功能的數據對outer()進行大量測試。

2

無法從外部函數對象獲取內部函數(請參閱其他答覆!)。然而,單元測試和閉包(至少對我而言)都是令人驚歎的開發人員性能改進。我們可以兼得嗎?我們可以單獨測試嵌套函數嗎?

不容易。

但是,使用python模塊解析器,ast或tokenizer來實現代碼本身,提取內部函數(通過嵌套的某些路徑),並允許測試從狀態運行它們爲函數(在測試目標中定義的)嵌套函數(封閉的名稱的值)和存根/模擬。

任何人都知道這樣的事情嗎?谷歌搜索未能找到任何東西。

1

我有同樣的疑問,並找到一種方法來獲取內部函數的測試。

def outer(): 
    def inner(): 
     pass 

    if __debug__: 
     test_inner(inner) 
     # return 

def test_inner(f): 
    f() # this calls the inner function 

outer() 

基本上,您可以將內部函數作爲參數發送到外部並根據需要進行測試。當調用outer()時,您的測試將運行,並且由於它是一個閉包,它將保留外部函數的任何額外屬性(如變量)。使用列表,您可以發送儘可能多的功能。要忽略如果,一個選項是運行這樣的代碼:

python -O code.py 
1

我已經寫了一個小的輔助模塊,它允許正是這一點:嵌套函數

例子:

def f(v1): 
    v2 = 1 
    def g(v3=2): 
    return v1 + v2 + v3 + 4 
    def h(): 
    return 16 
    return g() + h() + 32 

class C(object): 
    def foo(self): 
    def k(x): 
     return [ self, x ] 
    return k(3) 

def m(): 
    vm = 1 
    def n(an=2): 
    vn = 4 
    def o(ao=8): 
     vo = 16 
     return vm + an + vn + ao + vo 
    return o() 
    return n() 

這些可以使用這種類型的代碼進行單元測試:

import unittest 
from nested import nested 

class TestNested(unittest.TestCase): 
    def runTest(self): 
    nestedG = nested(f, 'g', v1=8, v2=1) 
    self.assertEqual(nestedG(2), 15) 
    nestedH = nested(f, 'h') 
    self.assertEqual(nestedH(), 16) 
    nestedK = nested(C.foo, 'k', self='mock') 
    self.assertEqual(nestedK(5), [ 'mock', 5 ]) 
    nestedN = nested(m, 'n', vm=1) 
    nestedO = nested(nestedN, 'o', vm=1, an=2, vn=4) 
    self.assertEqual(nestedO(8), 31) 

def main(argv): 
    unittest.main() 

if __name__ == '__main__': 
    import sys 
    sys.exit(main(sys.argv)) 

小助手模塊nested看起來像這樣:

import types 

def freeVar(val): 
    def nested(): 
    return val 
    return nested.__closure__[0] 

def nested(outer, innerName, **freeVars): 
    if isinstance(outer, (types.FunctionType, types.MethodType)): 
    outer = outer.func_code 
    for const in outer.co_consts: 
    if isinstance(const, types.CodeType) and const.co_name == innerName: 
     return types.FunctionType(const, globals(), None, None, tuple(
      freeVar(freeVars[name]) for name in const.co_freevars))