我有一个类,我通过提供类似parser = ParserClass('/path/to/file')
的文件名来实例化,然后我调用parser.parse()
方法打开并读取文件。
现在我想进行单元测试,如果内部发生了不好的事情:
with open(filename, 'rb') as fp:
// do something
将引发正确的异常,所以我想像这样嘲笑__builtin__.open
:
from mock import MagicMock, patch
from StringIO import StringIO
test_lines = StringIO("""some test lines, emulating a real file content""")
mock_open = MagicMock(return_value=test_lines)
with patch('__builtin__.open', mock_open):
self.mock.parse()
但这给了我一个AttributeError: StringIO instance has no attribute '__exit__'
我认为StringIO的行为与文件对象完全相同,但似乎并非如此。
如何使用带有模拟对象的给定内容(test_lines)测试此方法?我该怎么用?
答案 0 :(得分:10)
您可以将StringIO
子类化以提供上下文管理器:
class ContextualStringIO(StringIO):
def __enter__(self):
return self
def __exit__(self, *args):
self.close() # icecrime does it, so I guess I should, too
return False # Indicate that we haven't handled the exception, if received
test_lines = ContextualStringIO(...)
总体猜测:如果StringIO
对象是<{1}}对象的替代品,除了缺少上下文管理器,我想知道这是否有用:
file
class ContextualStringIO(StringIO, file):
pass
从ContextualStringIO
继承了它可以执行的文件操作,但其他所有内容都是从StringIO
继承的。它看起来很优雅,但可能需要进行大量测试(或者更熟悉Python内部的人来解释为什么这不起作用)。
答案 1 :(得分:8)
这是StringIO
未实现上下文管理器协议的已知问题。
from contextlib import contextmanager
@contextmanager
def StringIO():
"""Add support for 'with' statement to StringIO - http://bugs.python.org/issue1286
"""
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
sio = StringIO()
try:
yield sio
finally:
sio.close()
它为StringIO
实现了上下文管理器协议,并允许在with
语句中使用它。
UPDATE 好吧,我刚刚发现了mock_open
的存在,它可以直接从字符串中读取,所以它可能就是这样。
答案 2 :(得分:2)
或者,您可以使用io.StringIO
from the Standard Library:
io模块为流处理提供了Python接口。下 Python 2.x,这是作为内置文件的替代方案而提出的 对象,但在Python 3.x中,它是访问文件的默认接口 和溪流。
但是(正如Note所说),这只能用于unicode
类型。
答案 3 :(得分:0)
mock
库中有a provision专门用于此目的:
它确实缺少issue支持默认迭代器(即。__iter__
方法,所以你不能马上做for line in opened_mock_file
),但它可以作为描述here。
我赞成@ icecrime的回答,但我觉得他的更新部分似乎没有足够突出。