2016-02-21 64 views
4

我想嘲笑一個模塊級函數用於初始化類級(非實例)屬性。這裏有一個簡單的例子:Python:如何模擬類屬性初始化函數

# a.py  
def fn(): 
    return 'asdf' 

class C: 
    cls_var = fn() 

這裏是試圖嘲笑a.fn()一個單元測試:

# test_a.py 
import unittest, mock 
import a 

class TestStuff(unittest.TestCase): 
    # we want to mock a.fn so that the class variable 
    # C.cls_var gets assigned the output of our mock 

    @mock.patch('a.fn', return_value='1234') 
    def test_mock_fn(self, mocked_fn): 
     print mocked_fn(), " -- as expected, prints '1234'" 
     self.assertEqual('1234', a.C.cls_var) # fails! C.cls_var is 'asdf' 

我相信這個問題是where to patch但我已經試過進口兩種變化,沒有運氣。我甚至嘗試將import語句移入test_mock_fn(),以便在a.C進入作用域之前模擬的a.fn()將「存在」 - nope仍然失敗。

任何有識之士將不勝感激!

+0

您是否嘗試更改要從語句中使用的導入? 來自進口fn – Rainer

+0

嗨Ranier - 是的,試過了;沒有運氣。 (當我提到'......這兩個變體都是導入...'時,我應該已經更清楚了,模擬中的Python文檔給出了使用'import a'和'from import SomeClass'的例子,我嘗試了這兩種方法) –

回答

2

這裏實際發生的是,當你真正導入你的模塊時,fn()已經執行完畢。所以,在你已經評估了存儲在你的class屬性中的方法之後,模擬就進入了。

因此,當您嘗試模擬該方法時,您嘗試執行的測試爲時已晚。

,你甚至可以看到這種情況出現,如果你只是在你的方法添加一個print語句:

def fn(): 
    print("I have run") 
    return "asdf" 

在您的測試模塊,當您導入a和簡單,甚至沒有運行測試運行,你會看到I have run將出現在您的控制檯輸出中,而不會從您的a模塊明確運行任何內容。

所以,你可以在這裏採取兩種方法。要麼你可以使用PropertyMock的類屬性模擬出什麼你期望它來存儲,就像這樣:

@mock.patch('a.C.cls_var', new_callable=PropertyMock) 
def test_mock_fn(self, mocked_p): 
    mocked_p.return_value = '1234' 

    self.assertEqual('1234', a.C.cls_var) 

現在,你必須也意識到,通過這樣做,你實際上仍然運行fn,但有了這個嘲諷,你現在在cls_varPropertyMock中設置了'1234'。

以下建議(可能不太理想,因爲它需要設計更改)將需要修改爲什麼使用類屬性。因爲如果你真的把這個類屬性設置爲一個實例屬性,那麼當你創建一個C的實例時,那麼你的方法就會執行,在那個時候它將使用你的模擬。

所以,你的類是什麼樣子:

class C: 
    def __init__(self): 
     self.var = fn() 

和您的測試看起來像:

@mock.patch('a.fn', return_value='1234') 
def test_mock_fn(self, mocked_p): 
    self.assertEqual('1234', a.C().var) 
+0

Hi idjaw - 嘿,我喜歡'PropertyMock'的想法!我會嘗試。讓'fn()'運行並不理想,但值得一試。關於你的第二個想法,相信我很想重構這段代碼。但是,我給出的例子當然是涉及SQLAlchemy的真正問題的抽象。 SQLAlchemy廣泛使用了類的屬性,我很難做出改變。謝謝! –

0

即使@ idjaw的答案是正確的,正確地解釋什麼發生,我覺得最簡潔,簡單直接的方法是直接通過值來替代a.C.cls_var屬性,而不是模擬。

@mock.patch('a.C.cls_var', '1234') 

這足以讓你需要的一切:使用PropertyMock是有用的只是當你必須替換應該像一個屬性的屬性。

有關爲什麼你的方法不起作用的每個細節看看@ idjaw的答案。