首页 > 解决方案 > 根据 call_args 模拟 Popen 以返回不同的结果

问题描述

我正在调用一个def sftp(cmdstr, server):调用 Popen 来调用系统 sftp 客户端的函数。

我遇到的问题是我有一个使用不同参数main调用sftp两次的函数。第一次是文件ls,第二次是get文件。我已经模拟了调用,但我无法弄清楚如何分支返回值。

如果第一次调用我希望它在下面链接返回一个字符串。如果第二次调用我希望它使用 tmpdir.mkdir() 创建文件,那么文件“出现”就好像它们已被下载一样。由于稍后在代码中发生的操作,这些文件需要物理存在。

p_open = mocker.patch('mymodule.Popen', autospec=True)
mock_rv = mocker.Mock()
mock_rv.returncode = None
stdout_sftp = 'sftp> cd /upload\nsftp> ' \
    'ls\n{0}               {1}               \n{2}' \
    '               \nsftp> bye\n'.format(
        sftp_idf_file_list[0],
        sftp_idf_file_list[1],
        sftp_idf_file_list[2]
    )
mock_rv.communicate.return_value = [stdout_sftp, 'output_err']
p_open.return_value = mock_rv

任何人都知道如何制作: mock_rv.communicate.return_value = [stdout_sftp, 'output_err'] 引用一个仅在设置期间调用模拟而不是之前调用的函数?

还有一点需要注意的是,代码必须与 python2.7 和 3.6 兼容。

问候,

瑞安

标签: pythonpython-3.xpython-2.7pytest

解决方案


答案是使用 lambda & monkeypatch。

首先我们模拟对 Popen 的调用,这样我们就拥有了所有属性,然后我们设置了返回码,这样我们就不会在主代码中出错。

最后,我们在通信中设置 lambda,这是我们实际进行 sftp 调用的地方。如果我的调用中有'ls',我想要一个目录列表,所以我从 sftp 调用返回一个标准输出的字符串。另一方面,在这种情况下,如果没有“ls”,我想运行一个 get 命令,所以不是“getting”,而是执行一个在目标目录中创建文件的方法。

stdout_sftp = 'sftp> cd /upload\nsftp> ' \
    'ls\n{0}               {1}               \n{2}' \
    '               \nsftp> bye\n'.format(
        sftp_idf_file_list[0],
        sftp_idf_file_list[1],
        sftp_idf_file_list[2]
    )

def create_tmp_files(tdir, flist):
    """ Used to create temp files to simulate sftp"""
    for file_ in flist:
        current_file = tdir.join(file_)
        current_file.write('')
    return "stdout", "stderror"


monkeypatch.setattr('mymodule.Popen', mocker.Mock())
monkeypatch.setattr('mymodule.Popen.return_value.returncode',
                    None)
monkeypatch.setattr('mymodule.Popen.return_value.communicate',
                    lambda x: (stdout_sftp, "stderror")
                    if '\nls\n' in x
                    else create_tmp_files(
                        dst_dir,
                        file_list))

推荐阅读