尝试使用 pytest 和 capsys 访问标准输出时出现 ValueError

ValueError while trying to access stdout using pytest and capsys

正在尝试在验证用户帐户的对象中测试本地方法的标准输出。

例如,

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 今天很弱)。

如果我做了一些愚蠢的事情,我深表歉意,但此时我很担心键盘的安全,所以我想我会伸出援手。任何 help/suggestions 表示赞赏。你可以 运行 我发布的这个测试用例,它应该给你与发布的相同的错误。

您的问题是您提供了补丁装饰器,但没有将相应的参数添加到测试函数中。这是正确的:

@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)
    ...

您的代码中发生的事情是固定装置 testdircapsys 已被用作模拟参数(预期作为测试函数中的第一个参数),导致两者作为模拟对象而不是夹具。

旁注:如果pytest在这种情况下发出警告就好了,比如“你正在使用夹具名称作为修补对象的名称,也许你忘记添加修补对象参数?”...

请注意,我已将您示例代码中的断言替换为使用您的代码实际会通过的断言。