我正在使用 paramiko 编写一些函数来执行命令并在远程主机上创建文件。我想为它们编写一些单元测试,但我不知道实现此目的最简单的方法是什么?这是我设想的代码示例大纲:
import os
import paramiko
import pytest
def my_function(hostname, relpath='.', **kwargs):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname, **kwargs)
sftp = ssh.open_sftp()
sftp.chdir(relpath)
stdin, stdout, stderr = ssh.exec_command("echo hallo > test.txt")
@pytest.fixture("module")
def mock_remote_host():
# start a remote host here with a local test path
try:
yield hostname, testpath, {"username":"bob", "password":"1234"}
finally:
# delete the test path
# close the remote host
def test_my_function(mock_remote_host):
hostname, dirpath, kwargs = mock_remote_host
my_function(hostname, **kwargs)
filepath = os.path.join(dirpath, 'test.txt')
assert os.path.exists(filepath)
我已经查看了 paramiko 测试模块,但它们对于我的用例来说似乎相当复杂,我不知道如何简化它们。
我认为你真正需要模拟的是
paramiko.SSHClient
对象。您正在对函数 my_function
进行单元测试,您可以假设 paramiko
模块工作正常,并且您唯一需要进行单元测试的是 my_function
是否以正确的方式调用此 paramiko.SSHClient
的方法。
要模拟
paramiko.SSH
模块,您可以使用 unittest.mock 并用 test_my_function
装饰您的 @mock.patch.object(paramiko.SSHClient, sshclientmock)
函数。您必须首先将 sshclientmock
定义为某种 Mock
或 MagicMock
。
另外,在 python 2.7 中,有一些相当于
unittest.mock
的东西,但我不记得在哪里可以准确找到它。
编辑:正如@chepner 在评论中提到的。对于 python 2.7,您可以在
pypi中找到
mock
模块并使用 pip install mock
安装它
为了回答我自己的问题,我创建了:https://github.com/chrisjsewell/atomic-hpc/tree/master/atomic_hpc/mockssh。
正如自述文件中讨论的那样;它基于 https://github.com/carletes/mock-ssh-server/tree/master/mockssh 并基于 https://github.com/rspivak/ 进行了添加(以实现更多 sftp 功能) sftp服务器
还进行了以下更改:
users
参数,以便可以使用 private_path_key 或密码dirname
上下文管理器添加了 Server
参数,这样 this 将被设置为上下文持续时间内的根路径。paramiko.sftp_client.SFTPClient.chdir
以修复其与相对路径的使用。请参阅 test_mockssh.py 了解示例用法。
如果您想测试远程连接、远程文件系统结构和远程路径导航,您必须设置一个模拟主机服务器(可能是虚拟机)。换句话说,如果您想测试您在主机上的操作,您必须模拟主机。
如果您想使用主机的数据测试您的操作,最简单的方法似乎是按照其他答案中所说的 running.t 进行。
我同意 HraBal 的观点,因为 “基础设施即代码”。您可以将虚拟机视为一段代码。
例如:
target domain 127.0.0.1
target domain
并测试你想要的。我认为好处是你可以对所有编程语言执行此操作,而不需要重新发明轮子。此外,您和您的继任者将了解该系统的细节。
(我的英语不是很好)
我在我能找到的任何线程中都没有找到适合我的用例的任何好的解决方案。这似乎是最相关的,所以我将在这里发布我的解决方案。
我正在/正在为 Python 开发一个 LFTP 包装器,以通过高级代码实现即时数据流(这只是我从项目自述文件中提取的一个最小示例):
# Set the environment variables (only necessary in a non-interactive setting)
# If you are simply running this as a Python script,
# you can omit these lines and you will be prompted to set them interactively
import os
os.environ["PYREMOTEDATA_REMOTE_USERNAME"] = "username"
os.environ["PYREMOTEDATA_REMOTE_URI"] = "storage.example.com"
os.environ["PYREMOTEDATA_REMOTE_DIRECTORY"] = "/MY_PROJECT/DATASETS"
os.environ["PYREMOTEDATA_AUTO"] = "yes"
from pyremotedata.implicit_mount import IOHandler
handler = IOHandler()
with handler as io:
print(io.ls())
# The configuration is persistent, but can be removed using the following:
from pyremotedata.config import remove_config
remove_config()
但是,为这段代码编写单元测试非常困难,因为为了创建最真实的场景,我觉得我需要模拟 SFTP 服务器。长话短说,我发现最简单的方法是从 atmoz/sftp 启动 Docker 容器,并为其提供在测试期间生成和删除的临时 SSH 密钥。如果其他人的项目需要这个,可以这样做:
# SSH setup
mkdir -p /tmp # Ensure /tmp exists, mostly for sanity
ssh-keygen -t rsa -b 4096 -f /tmp/temp_sftp_key -q -N "" # Create a temporary SSH key
eval $(ssh-agent -s) # Start the ssh-agent
ssh-add /tmp/temp_sftp_key # Add the temporary SSH key to the ssh-agent
# echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV # Needed for GitHub Actions, not for local testing
# Mock SFTP server setup
mkdir -p /tmp/upload # Create a temporary directory which will serve as the root of the mock SFTP server file system
chmod ugo+rwx /tmp/upload # Set flexible permissions to avoid invalid permissions issues, which shouldn't arise with a properly set up production SFTP server
sudo docker run --name mock_sftp_server -p 0.0.0.0:2222:22 -d \ # Basic Docker Container setup
-v /tmp/temp_sftp_key.pub:/home/foo/.ssh/keys/temp_sftp_key.pub:ro \ # Add the temporary key to authorized_keys on the mock SFTP server
-v /tmp/upload:/home/foo/upload \ # Add the temporary SFTP server directory for file manipulation on the mock SFTP server
atmoz/sftp foo::1001 # Get the atmoz/sftp image and add a user "foo" without a password
for i in {1..10}; do # Tries to add the SSH fingerprints to known_hosts a few times, since the Docker Container may need a little time to come up and running properly
ssh-keyscan -p 2222 -H 0.0.0.0 >> ~/.ssh/known_hosts && break
echo "Waiting for SFTP server to be ready..."
sleep 1 # wait for 1 second before retrying
done
# Run your tests here
python3 -m unittest discover tests # or however else you want to run your tests
# Clean the environment
ssh-keygen -R 0.0.0.0 # Remove the SSH fingerprints from known_hosts
sudo docker stop mock_sftp_server # Stop the Docker Container
sudo docker rm mock_sftp_server # Remove the Docker Container
ssh-add -d /tmp/temp_sftp_key # Remove the temporary SSH key from the ssh-agent
rm /tmp/temp_sftp_key /tmp/temp_sftp_key.pub # Delete the temporary SSH key
eval "$(ssh-agent -k)" # Kill the ssh-agent
rm -rf /tmp/upload # Remove the temporary directory