Макетировать метод экземпляра с определенным возвращаемым значением?

У меня есть тестируемый метод, который выглядит так:

def execute_update(self):
    """Execute an update."""
    p = subprocess.Popen(['command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    try:
        stdout, stderr = p.communicate()

        if p.returncode == 0:
            # successful update, notify
            self.logger.info("Successfully updated.")
        else:
            # failed update, notify
            self.logger.error("Unable to update (code {returncode}):\n{output}".format(
                returncode=p.returncode, output=stdout))

    except KeyboardInterrupt as e:
        # terminate the process
        p.terminate()
        raise e

Я пытаюсь проверить его вызов Popen и вызов функций регистрации в unittest.TestCase метод испытания с mock:

@mock.patch.object(subprocess.Popen, 'communicate', autospec=True)
@mock.patch('updateservice.subprocess.Popen', autospec=True)
def test_fetch_metadata(self, mock_popen, mock_communicate):
    """Test that we can fetch metadata correctly."""
    mock_communicate.return_value = ("OUT", "ERR")
    mock_popen.returncode = 0

    self.reference.execute_update()

    # other asserts

Последняя строка терпит неудачу с:

    stdout, stderr = p.communicate()
ValueError: need more than 0 values to unpack

Что я делаю неправильно? У меня есть следующие требования:

  1. Проверьте, что конструктор subprocess.Popen был вызван с правильными значениями.
  2. Проверьте, что вызовы регистрации выполняются с выходным кодом и кодом возврата от процесса.

Номер два достаточно прост, я просто внедряю MagicMock в качестве объекта регистрации, но у меня проблемы с номером один.

1 ответ

Я думаю, что основная проблема исходит от вашего патча здесь:

@mock.patch.object(subprocess.Popen, 'communicate', autospec=True)

Как ни странно, создается впечатление, что тип создаваемого макета:

<class 'unittest.mock.NonCallableMagicMock'>

Это первый раз, когда я столкнулся с NonCallableMagicMock введите ранее, но, глядя на минимальную информацию, которую я нашел по этому вопросу, документы определяют это:

Часть, которая поднимает флаг для меня, находится здесь:

за исключением return_value и side_effect, которые не имеют никакого значения для не вызываемого макета.

Это потребовало бы дальнейшего исследования, чтобы определить, что именно это означает. Принимая это во внимание, и, может быть, вы уже попробовали это, следующее изменение в вашем unittest дает успешные результаты насмешки:

@mock.patch('server.upd.subprocess.Popen', autospec=True)
def test_fetch_metadata(self, mock_popen):
    """Test that we can fetch metadata correctly."""

    mock_popen.return_value = Mock()
    mock_popen_obj = mock_popen.return_value

    mock_popen_obj.communicate.return_value = ("OUT", "ERR")
    mock_popen_obj.returncode = 0

    self.reference.execute_update()

Итак, как вы можете видеть, мы в значительной степени создаем наш фиктивный объект в соответствии с mock_popen.return_value, Оттуда все остальное в значительной степени соответствует тому, что вы делали.

Другие вопросы по тегам