2014-03-02 72 views
0

考慮以下代碼:我如何自動模擬python 3默認爲None的模擬屬性?

import unittest 
from unittest.mock import patch 

class Foo(object): 
    def __init__(self, bar=None): 
     self.bar = bar 

    def methodA(self): 
     print("In methodA") 

    def methodB(self): 
     print("In methodB") 


def my_func(bar): 
    foo = Foo(bar) 
    if foo.bar: 
     foo.methodA() 
    foo.methodB() 


class MyTestCase(unittest.TestCase): 
    def test_my_func(self): 
     bar = None 

     with patch("__main__.Foo", autospec=True) as foo: 
      my_func(bar) 
      foo.methodB.assert_called_once_with() 


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

的想法是相當簡單的。我有一個函數,其行爲切換實例屬性的存在或不存在。我正在嘗試編寫一個單元測試,以驗證只執行某些Foo方法,具體取決於屬性。

基於模擬庫的patchautospeccing文檔,我想在patch()上下文管理器設置autospec=True就足夠了。它沒有。生成的Mock()正確包括methodAmethodB嘲笑,但測試失敗,此錯誤:

====================================================================== 
ERROR: test_my_func (__main__.MyTestCase) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "so.py", line 28, in test_my_func 
    my_func(bar) 
    File "trash.py", line 18, in my_func 
    if foo.bar: 
    File "/.../python3.3/unittest/mock.py", line 549, in __getattr__ 
    raise AttributeError("Mock object has no attribute %r" % name) 
AttributeError: Mock object has no attribute 'bar' 

我敢肯定,我失去了一些東西很明顯,但我似乎無法找出什麼。我怎麼能單元測試my_func()?我的問題

回答

0

部分是補丁的行爲誤解。我設置了上下文管理器返回__main__.Foo模擬的實例,而不是相同的實例my_func()使用。換一種方式,甚至當我能夠嘲笑Foo正常,無autospec,我不能在它的任何方法執行assert_called_once_with():這是不是同一個對象。

一種解決方案是嘲笑方法本身。這工作:

def test_my_func(self): 
    bar = None 
    with patch('__main__.Foo.methodB') as mock_methodB: 
     my_func(bar) 
     mock_methodB.assert_called_once_with() 

另一種方法是修改my_func()返回富:因爲函數返回測試模擬

def my_func(bar): 
    foo = Foo(bar) 
    if foo.bar: 
     foo.methodA() 
    foo.methodB() 
    return foo 

,下面應該工作:

def test_my_func(self): 
    bar = None 
    with patch('__main__.Foo', spec=True, bar=None): 
     foo = my_func(bar) 
     assert foo.methodB.called 
     assert not foo.methodA.called