問題描述
我應該如何自定義 unittest.mock.mock_open 來處理這段代碼?
How should I customize unittest.mock.mock_open to handle this code?
file: impexpdemo.py
def import_register(register_fn):
with open(register_fn) as f:
return [line for line in f]
我的第一次嘗試嘗試了 read_data
.
My first attempt tried read_data
.
class TestByteOrderMark1(unittest.TestCase):
REGISTER_FN = 'test_dummy_path'
TEST_TEXT = ['test text 1
', 'test text 2
']
def test_byte_order_mark_absent(self):
m = unittest.mock.mock_open(read_data=self.TEST_TEXT)
with unittest.mock.patch('builtins.open', m):
result = impexpdemo.import_register(self.REGISTER_FN)
self.assertEqual(result, self.TEST_TEXT)
這失敗了,大概是因為代碼沒有使用 read、readline 或 readlines.unittest.mock.mock_open 的 文檔 說,read_data 是一個字符串要返回的文件句柄的 read()、readline() 和 readlines() 方法.對這些方法的調用將從 read_data 獲取數據,直到數據耗盡.這些方法的模擬非常簡單.如果您需要更多控制您提供給測試代碼的數據,您需要自己自定義此模擬.默認情況下,read_data 是一個空字符串."
This failed, presumably because the code doesn't use read, readline, or readlines. The documentation for unittest.mock.mock_open says, "read_data is a string for the read(), readline(), and readlines() methods of the file handle to return. Calls to those methods will take data from read_data until it is depleted. The mock of these methods is pretty simplistic. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. read_data is an empty string by default."
由于文檔沒有提示需要什么樣的自定義,我嘗試了 return_value
和 side_effect
.都沒有用.
As the documentation gave no hint on what kind of customization would be required I tried return_value
and side_effect
. Neither worked.
class TestByteOrderMark2(unittest.TestCase):
REGISTER_FN = 'test_dummy_path'
TEST_TEXT = ['test text 1
', 'test text 2
']
def test_byte_order_mark_absent(self):
m = unittest.mock.mock_open()
m().side_effect = self.TEST_TEXT
with unittest.mock.patch('builtins.open', m):
result = impexpdemo.import_register(self.REGISTER_FN)
self.assertEqual(result, self.TEST_TEXT)
推薦答案
mock_open()
對象確實沒有實現迭代.
The mock_open()
object does indeed not implement iteration.
如果您不使用文件對象作為上下文管理器,您可以使用:
If you are not using the file object as a context manager, you could use:
m = unittest.mock.MagicMock(name='open', spec=open)
m.return_value = iter(self.TEST_TEXT)
with unittest.mock.patch('builtins.open', m):
現在 open()
返回一個迭代器,它可以像文件對象一樣直接迭代,它也可以與 next()
一起使用.但是,它不能用作上下文管理器.
Now open()
returns an iterator, something that can be directly iterated over just like a file object can be, and it'll also work with next()
. It can not, however, be used as a context manager.
您可以將它與 mock_open()
結合使用,然后在返回值上提供 __iter__
和 __next__
方法,額外的好處是 mock_open()
還添加了用作上下文管理器的先決條件:
You can combine this with mock_open()
then provide a __iter__
and __next__
method on the return value, with the added benefit that mock_open()
also adds the prerequisites for use as a context manager:
# Note: read_data must be a string!
m = unittest.mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: self
m.return_value.__next__ = lambda self: next(iter(self.readline, ''))
這里的返回值是從 file
對象 (Python 2) 或 MagicMock 對象/3/library/io.html#in-memory-streams" rel="noreferrer">內存中文件對象(Python 3),但只有read
、write
和 __enter__
方法已被刪除.
The return value here is a MagicMock
object specced from the file
object (Python 2) or the in-memory file objects (Python 3), but only the read
, write
and __enter__
methods have been stubbed out.
上述在 Python 2 中不起作用,因為 a) Python 2 期望 next
存在,而不是 __next__
和 b) next
是不被視為 Mock 中的特殊方法(正確地如此),所以即使您在上面的示例中將 __next__
重命名為 next
的 type返回值不會有 next
方法.對于大多數情況,讓文件對象產生一個可迭代而不是一個迭代器就足夠了:
The above doesn't work in Python 2 because a) Python 2 expects next
to exist, not __next__
and b) next
is not treated as a special method in Mock (rightly so), so even if you renamed __next__
to next
in the above example the type of the return value won't have a next
method. For most cases it would be enough to make the file object produced an iterable rather than an iterator with:
# Python 2!
m = mock.mock_open(read_data=''.join(self.TEST_TEXT))
m.return_value.__iter__ = lambda self: iter(self.readline, '')
任何使用 iter(fileobj)
的代碼都可以工作(包括 for
循環).
Any code that uses iter(fileobj)
will then work (including a for
loop).
Python 跟蹤器中的未解決問題旨在彌補這一差距.
這篇關于為迭代定制 unittest.mock.mock_open的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!