3

我需要测试一种打开两个文件并向每个文件写入不同数据的方法。文件的写入顺序无关紧要。

以下是我如何测试一种只需要打开一个文件的方法,使用Mock替换open

from io import BytesIO
import mock

class MemorisingBytesIO(BytesIO):
    """Like a BytesIO, but it remembers what its value was when it was closed."""
    def close(self):
        self.final_value = self.getvalue()
        super(MemorisingBytesIO, self).close()

open_mock = mock.Mock()
open_mock.return_value = MemorisingBytesIO()

with mock.patch('__builtin__.open', open_mock):
    write_to_the_file()  # the function under test

open_mock.assert_called_once_with('the/file.name', 'wb')
assert open_mock.return_value.final_value == b'the data'

我无法修改此方法以使用写入两个文件的方法。我考虑过使用顺序side_effect返回两个MemorisingBytesIOs,并断言它们中的每一个都包含正确的数据,但是测试会很脆弱:如果方法中调用的顺序发生变化,测试就会失败。

所以我真正想做的是在用一个文件名调用它时open_mock返回一个MemorisingBytesIO,当它用另一个文件名调用时返回一个不同的。我已经在其他语言的模拟库中看到了这一点:在没有子类化的情况下是否可以在 Python 中使用Mock

4

2 回答 2

1

下面的方法怎么样?(使用类属性保存文件内容):

from io import BytesIO
import mock

class MemorisingBytesIO(BytesIO):
    """Like a BytesIO, but it remembers what its value was when it was closed."""
    contents = {}
    def __init__(self, filepath, *args, **kwargs):
        self.filepath = filepath
        super(MemorisingBytesIO, self).__init__()
    def close(self):
        self.contents[self.filepath] = self.getvalue()
        super(MemorisingBytesIO, self).close()

def write_to_the_file():
    with open('a/b.txt', 'wb') as f:
        f.write('the data')
    with open('a/c.txt', 'wb') as f:
        f.write('another data')


#MemorisingBytesIO.contents.clear()
open_mock = mock.Mock(side_effect=MemorisingBytesIO)
with mock.patch('__builtin__.open', open_mock):
    write_to_the_file()  # the function under test

open_mock.assert_called_once_with('a/b.txt', 'wb')
open_mock.assert_called_once_with('a/c.txt', 'wb')
assert MemorisingBytesIO.contents['a/b.txt'] == b'the data'
assert MemorisingBytesIO.contents['a/c.txt'] == b'another data'
于 2013-08-25T12:39:23.327 回答
0

从那以后,我发现了使用mock. 你可以设置side_effectequals一个函数;当调用模拟时,该函数将传递参数。

In [1]: import mock

In [2]: def print_it(a, b):
   ...:     print b
   ...:     print a
   ...:     

In [3]: m = mock.Mock(side_effect=print_it)

In [4]: m('hello', 2)
2
hello

因此,这是您编写原始示例以使用两个文件的方式:

fake_file_1 = MemorisingBytesIO()
fake_file_2 = MemorisingBytesIO()

def make_fake_file(filename, mode):
    if filename == 'a/b.txt':
        return fake_file_1
    elif filename == 'a/c.txt':
        return fake_file_2
    else:
        raise IOError('Wrong file name, Einstein')

open_mock = mock.Mock(side_effect=make_fake_file)
with mock.patch('__builtin__.open', open_mock):
    write_to_the_file()

assert ('a/b.txt', 'wb') in open_mock.call_args
assert ('a/c.txt', 'wb') in open_mock.call_args
assert fake_file_1.final_value == 'file 1 data'
assert fake_file_2.final_value == 'file 2 data'
于 2013-08-26T21:23:58.877 回答