2015-10-26 100 views
3

看這個網頁:http://www.toptal.com/python/an-introduction-to-mocking-in-python - 作者談論了Python中的模擬和修補,並給出了一個非常可靠的「真實世界」的例子。絆倒我的部分是理解單元測試框架如何工作知道哪個模擬對象被傳遞給哪個補丁。@ mock.patch如何知道每個模擬對象使用哪個參數?

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

import os 
import os.path 

def rm(filename): 
    if os.path.isfile(filename): 
     os.remove(filename) 

代碼示例很容易理解。硬件編碼依賴於OS庫/模塊。如果文件中刪除使用os.remove()

測試/模擬代碼如下存在使用os.path.isfile()方法,如果是這樣,首先檢查:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 

from mymodule import rm 

import mock 
import unittest 

class RmTestCase(unittest.TestCase): 

    @mock.patch('mymodule.os.path') 
    @mock.patch('mymodule.os') 
    def test_rm(self, mock_os, mock_path): 
     # set up the mock 
     mock_path.isfile.return_value = False 

     rm("any path") 

     # test that the remove call was NOT called. 
     self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.") 

     # make the file 'exist' 
     mock_path.isfile.return_value = True 

     rm("any path") 

     mock_os.remove.assert_called_with("any path") 

我猜什麼的困惑我的是,有2個@Patch電話和2個參數在測試中通過。單元測試框架如何知道mymodule.os.path正在修補os.path並且它被映射到mock_pathmymodule.os.path在哪裏定義?

(似乎有很多「神奇」的事情,我不跟隨它。)

回答

1

它會被裝飾的執行順序和也是參數的順序傳遞到您的測試方法...

爲了裝飾的執行如下所示: http://thadeusb.com/weblog/2010/8/23/python_multiple_decorators

當您使用補丁你寫的方式,模擬實例會自動爲您創建並通過作爲參數給你的測試方法。還有它的另一個版本:

@mock.patch("subprocess.check_output", mock.MagicMock(return_value='True')) 
def test_mockCheckOutput(self): 
    self.assertTrue(subprocess.check_output(args=[])=='True') 

在這種情況下,你通過自己的模仿對象,並在這個例子中,當你調用subprocess.check_output(),它會返回「真」

但是你可以這樣做:

def test_mockCheckOutput(self): 
    m = mock.MagicMock(return_value='True') 
    with mock.patch("subprocess.check_output", m): 
     self.assertTrue(subprocess.check_output(args=[])=='True') 

,在這種情況下,你可以通過你想,因爲它會在運行時進行評估任何模擬項目...... :)

+0

好的,這解決了我的一半問題。 (實際上,我會說它是按倒序排列的,但是我在你鏈接的頁面上看到你的意思。)Next:Mock對象在哪裏創建?我看到我可以在測試中訪問它(因爲它是在我的裝飾器中傳遞的)。但是如果我想在明確的方法之外創建它,請在Setup()方法中說明呢?我將如何做到這一點? – Pretzel

+1

當您按照您編寫它的方式使用修補程序時,它會自動爲您創建。還有另一個版本: @ mock.patch(「subprocess.check_output」,mock.MagicMock(return_value ='True')) def test_mockCheckOutput(self): 在這種情況下,你傳遞你自己的Mock對象,並在這個例子中,當你調用subprocess.check_output()時,它會返回'True' – eyalsn

+0

隨意編輯你的答案,以便你的代碼顯示格式正確(以幫助任何其他人稍後閱讀。:)) – Pretzel

1

當應用裝飾,這是好事,看它這樣

<wrapper1> 
    <wrapper2> 
     <wrapper3> 
      **Your Function** 
     </wrapper3> 
    </wrapper2> 
</wrapper1> 

主要是需要你的函數順序與包裝互動:

wrapper3 - >wrapper2 - >wrapper1

@wrapper1 
@wrapper2 
@wrapper3 
def your_func(wrapper1.input, wrapper2.input, wrapper3.input): 

wrapper1.input是不是你如何實際參考其輸入

要回答你的問題的第二部分,mymodule.os知道如何參考os。修補時,您正在有效攔截對該特定名稱的呼叫。當您致電osmymodule您實際上呼叫mymodule.os。修補時,你必須參考正在被實際代碼調用的方式被模擬的類,而不是從測試模塊的角度來看

相關問題