2016-06-20 51 views
4

我遇到了一個問題,我認爲這可能是我使用的庫的一個錯誤。不過,我對python,unittest和unittest.mock庫相當陌生,所以這可能只是我理解中的一個漏洞。生產類構造函數需要額外參數時,爲什麼unittest.mock失敗?

同時加入測試,一些生產代碼,我遇到了一個錯誤,我已經產生再現這一問題的最少的樣品:

import unittest 
import mock 

class noCtorArg: 
    def __init__(self): 
     pass 
    def okFunc(self): 
     raise NotImplemented 


class withCtorArg: 
    def __init__(self,obj): 
     pass 
    def notOkFunc(self): 
     raise NotImplemented 
    def okWithArgFunc(self, anArgForMe): 
     raise NotImplemented 

class BasicTestSuite(unittest.TestCase): 
    """Basic test Cases.""" 

    # passes 
    def test_noCtorArg_okFunc(self): 
     mockSUT = mock.MagicMock(spec=noCtorArg) 
     mockSUT.okFunc() 
     mockSUT.assert_has_calls([mock.call.okFunc()]) 

    # passes 
    def test_withCtorArg_okWithArgFuncTest(self): 
     mockSUT = mock.MagicMock(spec=withCtorArg) 
     mockSUT.okWithArgFunc("testing") 
     mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")]) 

    # fails 
    def test_withCtorArg_doNotOkFuncTest(self): 
     mockSUT = mock.MagicMock(spec=withCtorArg) 
     mockSUT.notOkFunc() 
     mockSUT.assert_has_calls([mock.call.notOkFunc()]) 


if __name__ == '__main__': 
    unittest.main() 

如何我進行的測試,輸出如下:

E:\work>python -m unittest testCopyFuncWithMock 
.F. 
====================================================================== 
FAIL: test_withCtorArg_doNotOkFuncTest (testCopyFuncWithMock.BasicTestSuite) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "testCopyFuncWithMock.py", line 38, in test_withCtorArg_doNotOkFuncTest 
    mockSUT.assert_has_calls([mock.call.notOkFunc()]) 
    File "C:\Python27\lib\site-packages\mock\mock.py", line 969, in assert_has_calls 
    ), cause) 
    File "C:\Python27\lib\site-packages\six.py", line 718, in raise_from 
    raise value 
AssertionError: Calls not found. 
Expected: [call.notOkFunc()] 
Actual: [call.notOkFunc()] 

---------------------------------------------------------------------- 
Ran 3 tests in 0.004s 

FAILED (failures=1) 

我使用python 2.7.11,通過pip安裝模擬版本2.0.0。

對於我在做什麼的任何建議是錯誤的?或者這看起來像庫中的錯誤?

+0

不太清楚你的具體問題,但只是一個Python編碼注 - Python2.7類,你總是要繼承'對象',即'class WithCtorArg(object):'。此外,類名通常以大寫字母開頭(儘管我意識到可能剛剛在翻譯成您的MCVE時迷失了)。 – dwanderson

+0

謝謝@dwanderson。 –

回答

3

有趣的是,您選擇執行斷言的方式掩蓋了您的問題。

嘗試,而不是這樣的:

mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()]) 

做到這一點:

mockSUT.assert_has_calls(calls=[mock.call.notOkFunc()], any_order=True) 

你會看到實際的異常:

TypeError("'obj' parameter lacking default value") 

這是因爲你試圖實例類別withCtorArg的一個實例,其參數obj沒有發def lt值。如果你曾試圖居然直接實例化它,你會看到:

TypeError: __init__() takes exactly 2 arguments (1 given) 

不過,既然你讓mock庫處理模擬對象的實例化,錯誤發生在那裏 - 你得到了TypeError例外。

修改相關類:

class withCtorArg: 
    def __init__(self, obj = None): 
     pass 
    def notOkFunc(self): 
     pass 
    def okWithArgFunc(self, anArgForMe): 
     pass 

和OBJ增加一個默認值沒有解決的問題。

+0

是的,好點。我希望通過使用模擬,你不需要調用實際的構造函數,因爲它與SUT無關。不幸的是,在現實世界的例子中(至少在我的例子中)修改被嘲笑的類不是一個好主意,構造函數參數是有原因的,並且不是可選的,還有其他建議嗎? –

2

我不認爲我可以明確地解釋爲什麼會出現這種情況,我仍然懷疑Mock庫中存在一個錯誤,因爲這個問題只發生在被調用函數沒有參數的測試用例上。感謝advance512指出真正的錯誤是隱藏的!

然而,爲了解決這個問題,而不必修改生產代碼我會用下面的辦法:

# passes 
@mock.patch ('mymodule.noCtorArg') 
def test_noCtorArg_okFunc(self, noCtorArgMock): 
    mockSUT = noCtorArg.return_value 
    mockSUT.okFunc() 
    mockSUT.assert_has_calls([mock.call.okFunc()]) 

# passes 
@mock.patch ('mymodule.withCtorArg') 
def test_withCtorArg_okWithArgFuncTest(self, withCtorArgMock): 
    mockSUT = withCtorArg.return_value 
    mockSUT.okWithArgFunc("testing") 
    mockSUT.assert_has_calls([mock.call.okWithArgFunc("testing")]) 

# now passes 
@mock.patch ('mymodule.withCtorArg') 
def test_withCtorArg_doNotOkFuncTest(self, withCtorArgMock): 
    mockSUT = withCtorArg.return_value 
    mockSUT.notOkFunc() 
    mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True) 

編輯:此

一個問題是,模擬沒有spec設置。這意味着SUT能夠調用原始類定義中不存在的方法。

的另一種方法是包裝類來嘲笑:

class withCtorArg: 
    def __init__(self,obj): 
     pass 
    def notOkFunc(self): 
     raise NotImplemented 
    def okWithArgFunc(self, anArgForMe): 
     raise NotImplemented 

class wrapped_withCtorArg(withCtorArg): 
    def __init__(self): 
     super(None) 

class BasicTestSuite(unittest.TestCase): 
    """Basic test Cases.""" 

    # now passes 
    def test_withCtorArg_doNotOkFuncTest(self): 
     mockSUT = mock.MagicMock(spec=wrapped_withCtorArg) 
     mockSUT.notOkFunc() 
     #mockSUT.doesntExist() #causes the test to fail. "Mock object has no attribute 'doesntExist'" 
     assert isinstance (mockSUT, withCtorArg) 
     mockSUT.assert_has_calls([mock.call.notOkFunc()], any_order=True) 
+0

試圖實例化一個在其構造函數中需要參數的類的實例,而不給它參數,如果你試圖直接執行它,將會失敗。你爲什麼期望它在模擬工作,特別是MagicMock中自動「包裝」各種方法並監視你所做的調用以及它們是否有效? :)我會說,你看到的行爲是有道理的,除了庫中的錯誤。 – advance512

+0

我希望這個庫會包裝構造函數(就像我在這個答案中所做的那樣),因爲我們並不真的想要一個類的實例。我們只需要假裝一個實際上除了對所做的調用進行間諜以外的任何事情,並假冒返回值。在這種情況下,我們並不關心構造函數簽名是否匹配,因爲它將在SUT之外創建並注入。 –

+0

或者,如果在創建模擬時有一種方法可以提供構造函數參數,那麼它們可以被轉發給類構造函數。你知道如何用這個模擬庫來做到這一點嗎?我沒有發現任何有關創建具有構造函數參數的類的mock的文檔。感謝您的關注。 –

相關問題