9

我有一个类,我通过给出一个文件名来实例化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

4 回答 4

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的直接替代品,除了缺少上下文管理器,我想知道这是否可行:file

class ContextualStringIO(StringIO, file):
    pass

ContextualStringIO继承了它可以从哪些文件操作StringIO,但其他一切都继承自file. 它看起来很优雅,但可能需要大量测试(或者更熟悉 Python 内部的人来解释为什么这不起作用)。

于 2013-06-13T14:53:48.147 回答
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可以直接从字符串中读取,所以这可能是要走的路。

于 2013-06-13T14:55:29.350 回答
2

或者,您可以使用io.StringIO标准库中的

io 模块提供 Python 接口来进行流处理。在 Python 2.x 中,这是作为内置文件对象的替代方案而提出的,但在 Python 3.x 中,它是访问文件和流的默认接口。

但是(正如注释所说)这只能与unicode类型一起使用。

于 2013-06-13T15:59:19.310 回答
1

图书馆中有一个专门为此目的的规定:mock

它确实存在缺少对默认迭代器的支持的问题__iter__(即方法,所以你不能for line in opened_mock_file马上做),但它可以按照这里的描述来解决。

我赞成@icecrime 的回答,但我觉得他的更新部分似乎不够突出。

于 2017-01-19T07:29:02.657 回答