Allows you to test behavior that relies on external SFTP servers to function. This means you can "replace" the external server with a local, mocked version of it, testing behavior that relies on certain files being or not being present on said server.
We do this by locally modifying paramiko.Transport.__init__ to redirect real server addresses to internal mocked servers.
This was inspired by httmock's implementation, check them out!
You can use sftpmock both as a decorator, or as a context manager, like so:
# DECORATOR
@with_sftpmock({})
def test_function():
# Do your asserts here
# CONTEXT MANAGER
def test_function():
with SFTPMock({}):
# Do your asserts hereOn both of the methods, sftpmock expects a server_contents input dict. This dictionary follows this syntax:
{
"mockedserver.com": {
"folder": {
"sub_folder": {
"file.txt": "file content"
}
}
}
}The folder structure has no depth limit and can contain any number of files.
You may also mock more than one server if needed.
Normally you'd use sftpmock like this:
@with_sftpmock({
"test.com": {"a_folder": {"coisa.txt": "some text"}}
})
def test_function():
# Do your asserts hereIf you need to call paramiko.Transport directly on the test function, you need to import it on the function. This makes sure the mocked version of Transport is used.
On other use cases this is not necessary.
@with_sftpmock({
"test.com": {"a_folder": {"coisa.txt": "some text"}}
})
def test_function():
from paramiko import Transport
with Transport(("test.com", 22)) as transport:
# Do your asserts hereIf you need to mock more than one server, simply include it on the input dict.
@with_sftpmock({
"test.com": {"a_folder": {"coisa.txt": "some text"}},
"otherdomain.com": {"a_folder": {"another.txt": "another text"}}
})
def test_function():
# Do your asserts hereIf you use Python sockets, you may run into errors when the socket still tries to connect to the real server, even though sftpmock ignores that connection.
One way to circumvent that is by using unnitest.mock to prevent that initial socket connection. From there, sftpmock works as expected.
import socket
@with_sftpmock({
"test.com": {"a_folder": {"test.txt": "some text"}},
})
@mock.patch('socket.socket', spec=socket.socket)
def test_function(self, mock_socket):
from paramiko import Transport
mock_socket.return_value.getsockname.return_value = ("test.com", 22)
serversocket = socket.socket()
serversocket.connect(("test.com", 22))
with Transport(serversocket) as transport:
# Do your asserts here- Trying to test transport.sock hostname and ports may result in errors, as they are replaced by localhost and a random local port
- SSHClient may be replaced by a local server, as it also uses paramiko.Transport (need to test this still)
- If you are using sockets to init a Transport, you may need to mock them first
- If the SFTP servers being tested are actually localhost, this won't work
- Not depend on pytest-sftpserver package
- Allow for server port differentiation
- Avoid affecting SSHClient