如何使用Python中的模拟文件对象进行单元测试?

时间:2013-06-13 14:50:14

标签: python unit-testing python-2.7 mocking

我有一个类,我通过提供类似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)测试此方法?我该怎么用?

4 个答案:

答案 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未实现上下文管理器协议的已知问题。

common recipe如下:

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的回答,但我觉得他的更新部分似乎没有足够突出。