ValueError при попытке доступа к stdout с помощью pytest и capsys - PullRequest
0 голосов
/ 21 июня 2020

Попытка протестировать стандартный вывод локального метода в объекте, который проверяет учетную запись пользователя.

Например,

class Foo:
    def __init__(self, bar, baz):
        self.bar = bar
        if baz:
            self.something = self.load_something()
        else:
            print('Error initializing')

    def load_something(self):
        return ''

    def make_subprocess_call(self):
        return 'stdout', 'stderr'
    
    def confirm_something(self):
        subprocess_stdout, subprocess_stderr = self.make_subprocess_call()
        if subprocess_stdout:
            print('good output')
        else:
            print('err output', subprocess_stderr)

Затем для проверки:

from Foo_Class_File import Foo
import mock
import pytest

@mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'})
@mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr'))
def test_confirm_something(testdir, capsys):
    test_foo = Foo(testdir, True)
    test_foo.confirm_something()
    out, err = capsys.readouterr()
    assert out == 'more stdout'
    assert err == 'more_stderr'

Затем запуск:

python3 -m pytest test_foo_file.py

дает:

============================================================================== FAILURES ===============================================================================
_______________________________________________________________________ test_confirm_something ________________________________________________________________________

testdir = <MagicMock name='make_subprocess_call' id='140533346667632'>, capsys = <MagicMock name='load_something' id='140533346970400'>

    @mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'})
    @mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr'))
    def test_confirm_something(testdir, capsys):
        test_foo = Foo(testdir, True)
        test_foo.confirm_something()
>       out, err = capsys.readouterr()
E       ValueError: not enough values to unpack (expected 2, got 0)

blah.py:10: ValueError
------------------------------------------------------------------------ Captured stdout call -------------------------------------------------------------------------
good output
======================================================================= short test summary info =======================================================================
FAILED test_foo_file.py::test_confirm_something - ValueError: not enough values to unpack (expected 2, got 0)
========================================================================== 1 failed in 0.11s ==========================================================================

Я просто запутался, потому что он говорит, что захватывает стандартный вывод?

Я думаю, что это должно выполните создание экземпляра объекта, а затем вызов метода, поскольку у меня нет проблем с захватом с помощью capsys, если есть только объект, но как только я попытаюсь создать его экземпляр и вызвать метод, то, что вы увидите над ним, что произойдет. Я не смог найти подобный случай в Интернете или в документации, не из-за отсутствия попыток (или, возможно, мой google foo сегодня слаб).

Извините, если я делаю что-то глупое, но на данный момент я обеспокоен ради безопасности моей клавиатуры, и решил, что протяну руку. Любая помощь / предложения приветствуются. Вы можете запустить этот тестовый пример, который я опубликовал, он должен дать вам ту же ошибку, что и опубликованный.

1 Ответ

1 голос
/ 21 июня 2020

Ваша проблема в том, что вы предоставили декораторы патчей, но не добавили соответствующие аргументы в тестовую функцию. Это было бы правильно:

@mock.patch('Foo_Class_File.Foo.load_something', return_value = {'a': 'b'})
@mock.patch('Foo_Class_File.Foo.make_subprocess_call', return_value=('more stdout', 'more_stderr'))
def test_confirm_something(mocked_call, mocked_load, testdir, capsys):  # note the additional arguments
    test_foo = Foo(testdir, True)
    test_foo.confirm_something()
    out, err = capsys.readouterr()
    assert out == 'good output\n'
    assert err == ''

Или, альтернативно:

@mock.patch('Foo_Class_File.Foo.load_something')
@mock.patch('Foo_Class_File.Foo.make_subprocess_call')
def test_confirm_something(mocked_call, mocked_load, testdir, capsys):
    mocked_call.return_value = ('more stdout', 'more_stderr')
    mocked_load.return_value = {'a': 'b'}
    test_foo = Foo(testdir, True)
    ...

Что произошло в вашем коде, так это то, что фикстуры testdir и capsys использовались в качестве имитируемых параметров вместо этого (которые ожидаются в качестве первых аргументов в тестовой функции), что приводит к тому, что оба являются фиктивным объектом вместо фикстуры.

Боковое примечание : было бы неплохо, если бы pytest дал предупреждение в этом случае, что-то вроде «Вы используете имя прибора в качестве имени для исправленного объекта, возможно, вы забыли добавить аргумент исправленного объекта?» ...

Обратите внимание, что я заменил утверждения в ваш пример кода с утверждениями, которые действительно будут проходить с использованием вашего кода.

...