嘲讽FTP在单元测试
比方说,我要测试的这款哦,所以,复变函数:嘲讽FTP在单元测试
def func(hostname, username, password):
ftp = FTP(hostname, username, password)
ftp.retrbinary('RETR README', open('README', 'wb').write)
一项测试是:
@patch('FTP')
def test_func_happy_path():
mock_ftp = Mock()
mock_ftp.retrbinary = Mock()
MockFTP.return_value = mock_ftp()
func('localhost', 'fred', 's3Kr3t')
assert mock_ftp.retrbinary.called
然而,这将创建一个本地文件称为README,我显然不想要。
有没有办法模拟/修补open
这样就不会创建文件?
显然,作为工作的时候,我可以确保该文件被写入temporary directory,我可以作为参数传递给func
或内func
和回报创造。
注意,使用装饰@patch('__builtin__.open')
,以下预期上升:
self = <Mock name=u'open()' spec='FTP' id='51439824'>, name = 'write'
def __getattr__(self, name):
if name in ('_mock_methods', '_mock_unsafe'):
raise AttributeError(name)
elif self._mock_methods is not None:
if name not in self._mock_methods or name in _all_magics:
> raise AttributeError("Mock object has no attribute %r" % name)
AttributeError: Mock object has no attribute 'write'
我传递一个回调ftp.retrbinary
,而不是一个函数调用。
所以,考虑到你不关心你的开放会发生什么,你可以直接嘲笑它,所以它停止写作。要做到这一点,你可以按照类似的方法来模仿你的FTP
。因此,考虑到这一点,你可以设置你的测试代码是这样的:
import unittest
from mock import patch, Mock
from my_code import func
class SirTestsAlot(unittest.TestCase):
@patch('my_code.open')
@patch('my_code.FTP')
def test_func_happy_path(self, MockFTP, m_open):
MockFTP.return_value = Mock()
mock_ftp_obj = MockFTP()
m_open.return_value = Mock()
func('localhost', 'fred', 's3Kr3t')
assert mock_ftp_obj.retrbinary.called
assert m_open.called
# To leverage off of the other solution proposed, you can also
# check assert called with here too
m_open.assert_called_once_with('README', 'wb')
if __name__ == '__main__':
unittest.main()
正如你可以看到,我们在这里做的是,我们对于嘲讽的地方,我们正在测试。所以,考虑到这一点,我们嘲笑open
和FTP
相对于my_code
。
现在内my_code
,什么也没有改变:
from ftplib import FTP
def func(hostname, username, password):
ftp = FTP(hostname, username, password)
ftp.retrbinary('RETR README', open('README', 'wb').write)
运行该测试套件回来成功。
这工作正常!超。我会等待一段时间来接受它,以防万一别人想出一些黑巫术魔法......^_〜 – Sardathrion
@Sardathrion真棒!很高兴为你工作。祝你工作顺利。 – idjaw
@Sardathrion有什么特别的你想要做什么?我可以尝试一下。 :P – idjaw
另一种方法包括使用mock_open:
from unittest.mock import patch, mock_open
import ftplib
def func(hostname, username, password):
ftp = ftplib.FTP(hostname, username, password)
ftp.retrbinary('RETR README', open('README', 'wb').write)
@patch('ftplib.FTP')
def test_func_happy_path(MockFTP):
mock_ftp = MockFTP.return_value # returns another `MagicMock`
with patch('__main__.open', mock_open(), create=True) as m:
func('localhost', 'fred', 's3Kr3t')
assert mock_ftp.retrbinary.called
m.assert_called_once_with('README', 'wb')
test_func_happy_path()
或者代替书写到一个临时目录,你可以使用[StringIO的(https://docs.python.org/2/library/stringio.html)。避免写入磁盘并将其全部保存在内存中。我认为那是你的目标,对吗? – idjaw
是的,我实际上指的是使用'StringIO'代替开放的测试。这样你就避免了为单元测试写入磁盘。 – idjaw
我正在按我的方式查看您寻找的解决方案! :) *到编码机器* ... – idjaw