【问题标题】:mocking subprocess.Popen模拟 subprocess.Popen
【发布时间】:2018-11-19 10:04:10
【问题描述】:

我有一个模块 utils.py,它有这个 run_cmd() 方法

def run_cmd(cmd):
    pipe = subprocess.Popen(cmd,
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    print(pipe.communicate())
    print(pipe.returncode)
    stdout, stderr = [stream.strip() for stream in pipe.communicate()]
    output = ' - STDOUT: "%s"' % stdout if len(stdout) > 0 else ''
    error = ' - STDERR: "%s"' % stdout if len(stderr) > 0 else ''
    logger.debug("Running [{command}] returns: [{rc}]{output}{error}".format(
                 command=cmd,
                 rc=pipe.returncode,
                 output=output,
                 error=error))

    return pipe.returncode, stdout, stderr

我用 mock 写了一个单元测试,这个链接 stackoverflow 作为参考

  @patch('subprocess.Popen')
  @patch('utils.logger.debug')
  def test_run_cmd(self, mock_popen, mock_log):
    cmd = 'mock_command'
    mocked_pipe = Mock()
    attrs = {'communicate.return_value': ('output', 'error'), 'returncode': 0}
    mocked_pipe.configure_mock(**attrs)
    mock_popen.return_value = mocked_pipe
    log_calls = [call('Running [mock_command] returns: [0]outputerror')]
    utils.run_cmd(cmd)
    mock_popen.assert_called_once_with(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    mock_log.assert_has_calls(log_calls)

我在运行鼻子测试时得到了这个输出

        stdout, stderr = [stream.strip() for stream in pipe.communicate()]
ValueError: need more than 0 values to unpack
-------------------- >> begin captured stdout << ---------------------
<MagicMock name='Popen().communicate()' id='140197276165008'>
<MagicMock name='Popen().returncode' id='140197276242512'>

--------------------- >> end captured stdout << ----------------------
FAILED (errors=1)

为什么 pipe.communicate() 不打印 ('output', 'error') 或 pipe.returncode 不打印 0,而是它们的模拟方法?哪里出错了?我该如何解决这个问题?

【问题讨论】:

    标签: python python-2.7 mocking subprocess


    【解决方案1】:

    啊,你的问题已经有了答案。仔细看,你就会知道为什么。你必须像为logger 那样模拟。您在创建模拟对象时忘记提及utils

    @patch('utils.subprocess.Popen')
    

    现在,用多个值模拟嵌套函数,我认为你应该看看side_effectshere

    我还没有测试过下面的代码,但我希望它可以工作,或者至少给你一些线索。

    mocked_open.return_value.communicate.return_value = ('output', 'error')
    mocked_open.return_value.returncode = 0
    

    希望这会有所帮助!

    【讨论】:

    • 我可以这样模拟 pipe.communicate() 。 @patch('utils.subprocess.Popen.communicate', return_value=('output', 'error')) 如何模拟 pipe.returncode?​​span>
    • 更新答案以使用side_effects 和其他
    • 您好,感谢您的建议。我终于解决了。但是我修补了 subprocess 而不是 subprocess.Popen。 mock_subprocess.Popen.return_value.communicate.return_value = ('output', 'error')mock_subprocess.Popen.return_value.returncode = 0
    • 太棒了。你成功了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 2018-03-20
    • 2014-04-23
    • 2019-01-20
    • 2011-06-28
    • 2015-07-19
    • 2014-12-01
    相关资源
    最近更新 更多