模拟subprocess.Popen依赖于导入样式


问题内容

尝试模拟Popen时,如果子流程的导入在单元测试代码和主模块代码中均匹配,则只能使其成功。

给定以下模块listdir.py:

from subprocess import Popen, PIPE

def listdir(dir):
    cmd = ['ls', dir]
    pc = Popen(cmd, stdout=PIPE, stderr=PIPE)
    out, err = pc.communicate()
    if pc.returncode != 0:
        raise Exception
    return out

并遵循单元测试代码test_listdir.py

import subprocess
import listdir
import mock

@mock.patch.object(subprocess, 'Popen', autospec=True)
def test_listdir(mock_popen):
    mock_popen.return_value.returncode = 0
    mock_popen.return_value.communicate.return_value = ("output", "Error")
    listdir.listdir("/fake_dir")

由于某些原因,由于两个python模块之间的导入样式不同,因此无法模拟Popen,并且运行测试始终会引发异常。

如果我更改listdir.py以导入所有子过程,例如

import subprocess

def listdir(dir):
    cmd = ['ls', dir]
    pc = subprocess.Popen(cmd, stdout=subprocess.PIPE, 
                          stderr=subprocess.PIPE)
    out, err = pc.communicate()
    if pc.returncode != 0:
        raise ListingErrorException
    return out

然后在测试中返回“输出”。

任何人都想弄清楚为什么,我更喜欢在两个模块中都从子进程导入Popen,Pipe,但是我只是无法模拟它。


问题答案:

您需要在listdir中修补Popen的副本,而不是刚刚导入的副本。因此@mock.patch.object(subprocess, 'Popen', autospec=True),请尝试@mock.patch.object(listdir, 'Popen', autospec=True)

有关更多信息,请参见此文档:http : //www.voidspace.org.uk/python/mock/patch.html#where-to-
patch