如何測試與嘲笑下面的代碼(使用嘲弄,貼片裝飾和哨兵由Michael Foord's Mock framework提供):如何在with語句中使用open(在Python中使用Mock框架)?
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
如何測試與嘲笑下面的代碼(使用嘲弄,貼片裝飾和哨兵由Michael Foord's Mock framework提供):如何在with語句中使用open(在Python中使用Mock框架)?
def testme(filepath):
with open(filepath, 'r') as f:
return f.read()
做到這一點的方式,其中終於支持嘲諷蟒蛇模擬0.7.0已經改變協議方法(魔術方法),特別是使用MagicMock:
http://www.voidspace.org.uk/python/mock/magicmock.html
嘲笑開放爲上下文管理器(從實施例頁模擬文檔中)的一個例子:
>>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
... mock_open.return_value = MagicMock(spec=file)
...
... with open('/some/path', 'w') as f:
... f.write('something')
...
<mock.Mock object at 0x...>
>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')
哇!這看起來比目前在http://www.voidspace.org.uk/python/mock/magicmock.html的context-manager示例簡單得多,它明確地設置'__enter__'和'__exit__'來模擬對象 - 是後者方法過時,還是有用? – 2011-05-24 16:18:40
「後一種方法」展示瞭如何在沒有*的情況下使用MagicMock(即它只是Mock如何支持魔術方法的一個例子)。如果您使用MagicMock(如上所述),則會爲您預先配置__enter__和__exit__。 – fuzzyman 2011-06-06 19:15:34
你可以指向你的[博客文章](http://www.voidspace.org.uk/python/weblog/arch_d7_2010_10_02.shtml#e1188),在那裏你更詳細地解釋爲什麼/如何工作 – Rodrigue 2011-06-23 19:26:30
隨着模擬的最新版本,你可以使用真正有用mock_open幫手:
mock_open(模擬=無了read_data =無)
一個輔助函數來創建一個 嘲笑取代使用開放。它適用於直接調用open或 用作上下文管理器。
模擬參數是要配置的模擬對象。如果無(默認爲 ),則將爲您創建MagicMock,並將API 限制爲標準文件句柄上提供的方法或屬性。
read_data是一個字符串,用於讀取文件句柄的方法,返回 。這是一個默認的空字符串。
>>> from mock import mock_open, patch
>>> m = mock_open()
>>> with patch('{}.open'.format(__name__), m, create=True):
... with open('foo', 'w') as h:
... h.write('some stuff')
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
要爲簡單的文件read()
使用mock_open(原mock_open片斷already given on this page是面向更寫):
my_text = "some text to return when read() is called on the file object"
mocked_open_function = mock.mock_open(read_data=my_text)
with mock.patch("__builtin__.open", mocked_open_function):
with open("any_string") as f:
print f.read()
注每文檔爲mock_open如,這是專門爲read()
,所以不會像for line in f
這樣的常見模式工作。
使用Python 2.6.6/1.0.1模擬
看起來不錯,但我無法讓它與'for_open_file:'類型的代碼一起工作。我試着用實現'__iter__'的iterable StringIO進行實驗,並使用它來代替'my_text',但沒有運氣。 – 2015-01-15 02:26:00
@EvgeniiPuchkaryov這是專門爲'read()'工作的,所以在你的'for line for open_file'情況下不起作用;我已經編輯了這篇文章,以闡明 – jlb83 2015-01-15 18:38:08
@EvgeniiPuchkaryov'在f中的行:'可以通過將'open()'的返回值模擬爲[一個StringIO對象]來實現支持(http://stackoverflow.com/a /696485分之24325868)。 – 2015-09-10 23:45:14
我可能會有點晚了比賽,但這個調用另一個模塊open
時無需創建一個新的文件爲我工作。
test.py
import unittest
from mock import Mock, patch, mock_open
from MyObj import MyObj
class TestObj(unittest.TestCase):
open_ = mock_open()
with patch.object(__builtin__, "open", open_):
ref = MyObj()
ref.save("myfile.txt")
assert open_.call_args_list == [call("myfile.txt", "wb")]
MyObj.py
class MyObj(object):
def save(self, filename):
with open(filename, "wb") as f:
f.write("sample text")
通過修補__builtin__
模塊內的open
功能我mock_open()
,我可以嘲笑,而無需創建一個寫入文件。
注意:如果您使用的是使用cython的模塊,或者您的程序以任何方式依賴於cython,則需要導入cython's __builtin__
module,其中包括文件頂部的import __builtin__
。如果您使用的是cython,您將無法模擬通用的__builtin__
。
這種方法的一個變種對我來說很有效,因爲大多數被測代碼位於其他模塊中,如下所示。我確實需要確保將'import __builtin__'添加到我的測試模塊中。這篇文章有助於澄清爲什麼這種技術的工作原理和它一樣:http://www.ichimonji10.name/blog/6/ – killthrush 2015-11-21 01:59:52
這些答案中有很多噪音;幾乎所有的都是正確的,但過時而且不整齊。 mock_open
是mock
框架的一部分,使用非常簡單。用作上下文的patch
返回用於替換已修補的對象的對象:您可以使用它來使測試更簡單。
使用builtins
而不是__builtin__
。
from unittest.mock import patch, mock_open
with patch("builtins.open", mock_open(read_data="data")) as mock_file:
assert open("path/to/open").read() == "data"
mock_file.assert_called_with("path/to/open")
mock
不是unittest
一部分,你應該修補__builtin__
from mock import patch, mock_open
with patch("__builtin__.open", mock_open(read_data="data")) as mock_file:
assert open("path/to/open").read() == "data"
mock_file.assert_called_with("path/to/open")
,如果您使用mock_open()
的結果作爲使用patch
作爲裝飾new
patch
的論點可以b有點奇怪。
在這種情況下,最好使用new_callable
patch
的說法,並記住每一個額外的參數,作爲在patch
documentation描述patch
不使用將被傳遞給new_callable
功能。
patch()採用任意關鍵字參數。這些將被傳遞給施工中的模擬(或new_callable)。
例如裝飾版本Python 3.x都有是:
@patch("builtins.open", new_callable=mock_open, read_data="data")
def test_patch(mock_file):
assert open("path/to/open").read() == "data"
mock_file.assert_called_with("path/to/open")
請記住,在這種情況下patch
將增加模擬對象作爲參數的您測試功能。
對不起,請問'with patch(「builtins.open」,mock_open(read_data =「數據「))作爲mock_file:'被轉換成裝飾器語法?我試過了,但我不確定我需要傳遞給@patch(「builtins.open」,...)作爲第二個參數。 – 2017-05-04 18:20:20
@DrunkenMaster更新..謝謝指出。在這種情況下使用裝飾器並不重要。 – 2017-05-04 20:24:24
格拉齊!我的問題有點複雜(我必須將'mock_open'的'return_value'引導到另一個模擬對象中,並聲明第二個模擬的'return_value'),但是它通過將'mock_open'添加爲'new_callable'來工作。 – 2017-05-04 22:25:03
最常見的答案很有用,但我稍微擴展了一下。
如果你想基於傳遞給open()
的參數設置你的文件對象(在as f
的f
)的值,這裏有一個方法來做到這一點:
def save_arg_return_data(*args, **kwargs):
mm = MagicMock(spec=file)
mm.__enter__.return_value = do_something_with_data(*args, **kwargs)
return mm
m = MagicMock()
m.side_effect = save_arg_return_array_of_data
# if your open() call is in the file mymodule.animals
# use mymodule.animals as name_of_called_file
open_name = '%s.open' % name_of_called_file
with patch(open_name, m, create=True):
#do testing here
基本上,open()
會返回一個對象, with
將在該對象上調用__enter__()
。
爲了正確模擬,我們必須模擬open()
返回一個模擬對象。然後該模擬對象應該模擬__enter__()
調用(MagicMock
將爲我們執行此操作)以返回我們想要的模擬數據/文件對象(因此mm.__enter__.return_value
)。這樣做2嘲笑上面的方式允許我們捕獲傳遞給open()
的參數,並將它們傳遞給我們的do_something_with_data
方法。
我通過一個完整的模擬文件作爲字符串open()
和我do_something_with_data
是這樣的:
def do_something_with_data(*args, **kwargs):
return args[0].split("\n")
這把字符串轉換爲一個列表,以便您可以像使用普通文件執行以下操作:
for line in file:
#do action
如果正在測試的代碼以不同的方式處理文件,例如通過調用其函數「readline」,則可以使用所需屬性在函數「do_something_with_data」中返回所需的任何模擬對象。 – user3289695 2017-09-06 13:12:32
@Daryl Spitzer:你能否拋棄元問題(「我知道答案......」)這很混亂。 – 2009-08-17 19:34:27
在過去,當我離開時,人們抱怨說我正在回答我自己的問題。我會嘗試將其移至我的答案。 – 2009-08-17 19:38:16
@Daryl:避免回答自己的問題(通常源自「業障嫖娼」的憂慮)的投訴的最佳方式是將問題和/或答案標記爲「社區wiki」。 – 2009-08-17 19:43:59