您好,登錄后才能下訂單哦!
這篇文章主要講解了Python在單元測試中給對象打補丁的方法,內容清晰明了,對此有興趣的小伙伴可以學習一下,相信大家閱讀完之后會有幫助。
問題
你寫的單元測試中需要給指定的對象打補丁, 用來斷言它們在測試中的期望行為(比如,斷言被調用時的參數(shù)個數(shù),訪問指定的屬性等)。
解決方案
unittest.mock.patch()
函數(shù)可被用來解決這個問題。 patch()
還可被用作一個裝飾器、上下文管理器或單獨使用,盡管并不常見。 例如,下面是一個將它當做裝飾器使用的例子:
from unittest.mock import patch import example @patch('example.func') def test1(x, mock_func): example.func(x) # Uses patched example.func mock_func.assert_called_with(x)
它還可以被當做一個上下文管理器:
with patch('example.func') as mock_func: example.func(x) # Uses patched example.func mock_func.assert_called_with(x)
最后,你還可以手動的使用它打補?。?/p>
p = patch('example.func') mock_func = p.start() example.func(x) mock_func.assert_called_with(x) p.stop()
如果可能的話,你能夠疊加裝飾器和上下文管理器來給多個對象打補丁。例如:
@patch('example.func1') @patch('example.func2') @patch('example.func3') def test1(mock1, mock2, mock3): ... def test2(): with patch('example.patch2') as mock1, \ patch('example.patch3') as mock2, \ patch('example.patch4') as mock3: ...
討論
patch()
接受一個已存在對象的全路徑名,將其替換為一個新的值。 原來的值會在裝飾器函數(shù)或上下文管理器完成后自動恢復回來。 默認情況下,所有值會被 MagicMock
實例替代。例如:
>>> x = 42 >>> with patch('__main__.x'): ... print(x) ... <MagicMock name='x' id='4314230032'> >>> x 42 >>>
不過,你可以通過給 patch()
提供第二個參數(shù)來將值替換成任何你想要的:
>>> x 42 >>> with patch('__main__.x', 'patched_value'): ... print(x) ... patched_value >>> x 42 >>>
被用來作為替換值的 MagicMock
實例能夠模擬可調用對象和實例。 他們記錄對象的使用信息并允許你執(zhí)行斷言檢查,例如:
>>> from unittest.mock import MagicMock >>> m = MagicMock(return_value = 10) >>> m(1, 2, debug=True) 10 >>> m.assert_called_with(1, 2, debug=True) >>> m.assert_called_with(1, 2) Traceback (most recent call last): File "<stdin>", line 1, in <module> File ".../unittest/mock.py", line 726, in assert_called_with raise AssertionError(msg) AssertionError: Expected call: mock(1, 2) Actual call: mock(1, 2, debug=True) >>> >>> m.upper.return_value = 'HELLO' >>> m.upper('hello') 'HELLO' >>> assert m.upper.called >>> m.split.return_value = ['hello', 'world'] >>> m.split('hello world') ['hello', 'world'] >>> m.split.assert_called_with('hello world') >>> >>> m['blah'] <MagicMock name='mock.__getitem__()' id='4314412048'> >>> m.__getitem__.called True >>> m.__getitem__.assert_called_with('blah') >>>
一般來講,這些操作會在一個單元測試中完成。例如,假設你已經(jīng)有了像下面這樣的函數(shù):
# example.py from urllib.request import urlopen import csv def dowprices(): u = urlopen('http://finance.yahoo.com/d/quotes.csv?s=@^DJI&f=sl1') lines = (line.decode('utf-8') for line in u) rows = (row for row in csv.reader(lines) if len(row) == 2) prices = { name:float(price) for name, price in rows } return prices
正常來講,這個函數(shù)會使用 urlopen()
從Web上面獲取數(shù)據(jù)并解析它。 在單元測試中,你可以給它一個預先定義好的數(shù)據(jù)集。下面是使用補丁操作的例子:
import unittest from unittest.mock import patch import io import example sample_data = io.BytesIO(b'''\ "IBM",91.1\r "AA",13.25\r "MSFT",27.72\r \r ''') class Tests(unittest.TestCase): @patch('example.urlopen', return_value=sample_data) def test_dowprices(self, mock_urlopen): p = example.dowprices() self.assertTrue(mock_urlopen.called) self.assertEqual(p, {'IBM': 91.1, 'AA': 13.25, 'MSFT' : 27.72}) if __name__ == '__main__': unittest.main()
本例中,位于 example
模塊中的 urlopen()
函數(shù)被一個模擬對象替代, 該對象會返回一個包含測試數(shù)據(jù)的 ByteIO()
還有一點,在打補丁時我們使用了 example.urlopen
來代替 urllib.request.urlopen
。 當你創(chuàng)建補丁的時候,你必須使用它們在測試代碼中的名稱。 由于測試代碼使用了 from urllib.request import urlopen
,那么 dowprices()
函數(shù) 中使用的 urlopen()
函數(shù)實際上就位于 example
模塊了。
看完上述內容,是不是對Python在單元測試中給對象打補丁的方法有進一步的了解,如果還想學習更多內容,歡迎關注億速云行業(yè)資訊頻道。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。