I want to test that the ftp.storbinary()
function is called with the right arguments. This however is part of a different module from the one were the test live.
In the io.py
module I have this method (which is part of a class):
def to_ftp(self, output_path, file_name, host, username, password):
"""Upload file to FTP server."""
ftp = ftplib.FTP(host)
ftp.login(username, password)
full_path = os.path.join(output_path, file_name)
with open(full_path, "r") as ftp_file:
ftp.storbinary(" ".join(["STOR", file_name]), ftp_file.read)
I've create a test_io.py
module where I have a series of unittests. I was thinking to patch both open
and ftplib.FTP
, because I'm passing to ftp.storbinary()
ftp_file.read()
.
@patch("ppc_model.io.ftplib.FTP")
def test_store_call(self, mock_ftp):
"""The *storbinary* call must use the right arguments."""
with patch("ppc_model.io.open", mock_open(read_data=None)) as m:
self.writer.to_ftp(output_path="./output", file_name="output.zip",
host="myftp", username="username", password="password")
mock_ftp.return_value.storbinary.assert_called_once_with(
"STOR output.zip", m.read())
This however returns an AttributeError because the module doesn't have an attribute open
. How can I make sure Mock understand I'm trying to mock the builtin function?
Is there also a better way to test I'm passing the right arguments to ftp.storbinary
? I'm kinda new to mocking.
EDIT: I've made some progress. I think the issue was that I was trying to patch the wrong object. With open
I think I have to patch builtins.open
. A bit counter-intuitive.
@patch("ppc_model.io.ftplib.FTP")
def test_store_call(self, mock_ftp):
"""The *storbinary* call must use the right arguments."""
with patch("builtins.open", mock_open(read_data=None), create=True) as m:
self.writer.to_ftp(output_path="./output", file_name="output.zip",
host="myftp", username="username", password="password")
mock_ftp.return_value.storbinary.assert_called_once_with(
"STOR output.zip", m.read())
Unfortunately now the compiler is complaining about the fact that the Mock object doesn't have a read
method.
AttributeError: Mock object has no attribute 'read'
EDIT 2: Following RedCraig's advice I've patched the open
method in the local namespace and passed to ftp.storbinary
ftp_file instead of ftp_file.read()). So the current unit test is:
@patch("ppc_model.io.ftplib.FTP")
def test_store_call(self, mock_ftp):
"""The *storbinary* call must use the right arguments."""
with patch("{}.open".format(__name__), mock_open(read_data=None),
create=True) as m:
self.writer.to_ftp(output_path="./output", file_name="output.zip",
host="myftp", username="username", password="password")
mock_ftp.return_value.storbinary.assert_called_once_with(
"STOR output.zip", m)
And the code I'm trying to test:
def to_ftp(self, output_path, file_name, host, username, password):
"""Upload file to FTP server."""
ftp = ftplib.FTP(host)
ftp.login(username, password)
full_path = os.path.join(output_path, file_name)
with open(full_path, "r") as ftp_file:
ftp.storbinary(" ".join(["STOR", file_name]), ftp_file)
The current error I get is:
AssertionError: Expected call: storbinary('STOR output.zip', <MagicMock name='open' spec='builtin_function_or_method' id='140210704581632'>)
Actual call: storbinary('STOR output.zip', <_io.TextIOWrapper name='./output/output.zip' mode='r' encoding='UTF-8'>)