【问题标题】:python unit testing mocking Popen and Popen.communicatepython单元测试模拟Popen和Popen.communicate
【发布时间】:2019-05-15 23:47:07
【问题描述】:

我正在尝试编写一个执行子进程调用的 python unittest,所以我想模拟该调用。

我已经解决了这些 SO 问题(无济于事):

benchmark.py

from subprocess import Popen, PIPE, STDOUT

def some_func():

  with Popen(some_list, stdout=PIPE, stderr=STDOUT) as process:

    stdout, stderr = process.communicate(timeout=timeout)

test.py

import mock

@mock.patch('benchmark.Popen.communicate')
@mock.patch('benchmark.Popen')
def test_some_func(self, mock_popen, mock_comm):

  mock_popen.return_value = 0
  mock_comm.return_value = ('output', 'error')

  foo = benchmark.some_func()

运行单元测试时,我得到:

    stdout, stderr  = process.communicate(timeout=timeout)
ValueError: not enough values to unpack (expected 2, got 0)

看起来我没有正确地模拟communicate 的返回值;我做错了什么?

解决方案

我拿了 cmets 并提出了解决此类问题的建议答案:

test.py

import mock

@mock.patch('benchmark.Popen')
def test_some_func(self, mock_popen):

  process = mock_popen.return_value.__enter__.return_value
  process.returncode = 0
  process.communicate.return_value = (b'some output', b'some error')

  foo = benchmark.some_func()

【问题讨论】:

  • 您需要模拟 上下文管理器process 将是 mock_popen.return_value.__enter__.return_value
  • 我尝试做同样的事情,但出现以下错误:AssertionError: != 0

标签: python unit-testing mocking


【解决方案1】:

正如 jonrsharpe 所提到的,with Popen(...) as process 使用Popen 实例作为上下文管理器,它调用__enter__ 方法并将其值分配给process

jonsharpe 的解决方案使用 return_value 的魔法 mock 并且效果很好。但是您也可以实现一个上下文管理器并将模拟逻辑包装在其中:

import mock
import subprocess


class MockedPopen:

    def __init__(self, args, **kwargs):
        self.args = args
        self.returncode = 0

    def __enter__(self):
        return self

    def __exit__(self, exc_type, value, traceback):
        pass

    def communicate(self, input=None, timeout=None):
        if self.args[0] == 'ls':
            stdout = '\n'.join(['hello.txt', 'world.txt'])
            stderr = ''
            self.returncode = 0
        else:
            stdout = ''
            stderr = 'unknown command'
            self.returncode = 1

        return stdout, stderr


@mock.patch('subprocess.Popen', MockedPopen)
def foo():
    with subprocess.Popen(['ls']) as proc:
        stdout, stderr = proc.communicate()
        print(stdout, stderr)


foo()

输出:

hello.txt
world.txt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    • 2017-11-15
    相关资源
    最近更新 更多