модульное тестирование Python издевательства над Popen и Popen.communicate - PullRequest
0 голосов
/ 14 декабря 2018

Я пытаюсь написать юнит-тест на python, который выполняет вызов подпроцесса, поэтому я хотел бы посмеяться над этим вызовом.

Я уже прошел эти вопросы 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()

При запуске unittest я получаю:

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

Похоже, я не высмеиваю возвращаемое значениеcommunicate правильно;что я делаю не так?

решение

Я взял комментарии и предложил ответы для решения таких вопросов:

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()

1 Ответ

0 голосов
/ 15 декабря 2018

Как упоминал jonrsharpe, with Popen(...) as process использует экземпляр Popen в качестве диспетчера контекста, который вызывает метод __enter__ и присваивает его значение process.

Решение Jonsharpe использует return_value magic of 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...