使pytest的’capsys’ fixture在使用-s选项与否时,都能对待stdout相同。

huangapple go评论59阅读模式
英文:

Make pytest 'capsys' fixture treat stdout the same regardless of whether or not the -s option is used

问题

我使用pytestcapsys装置来检查stdout的文本输出是否符合预期。

我的问题是,当我使用-s选项运行测试时,断言会通过。但是当我不使用-s时,断言会失败,因为stdout中的一些文本被换行,意味着实际文本不再与预期文本相同。

是否有办法让capsys装置每次都像使用-s时一样捕获stdout

示例代码

import argparse
import pytest


class Args:

    class _HelpFormatter(argparse.HelpFormatter):

        def __init__(self, prog: str) -> None:
            super().__init__(prog, max_help_position=50)

    def __init__(self) -> None:

        global_parser = argparse.ArgumentParser(add_help=False, prog='wwaz',
            formatter_class=self._HelpFormatter)
        global_group = global_parser.add_argument_group('Global Options')
        global_group.add_argument('--help', action='help',
            help='Show this help message and exit.')
        global_group.add_argument('--verbose', action='store_true',
            default=False, help='Whether to output verbose logs. Also whether to show verbose test output. (default: False)')

        self.parser = argparse.ArgumentParser(add_help=False, prog='wwaz',
            formatter_class=self._HelpFormatter, parents=[global_parser], 
            description='Run routine Azure actions.')

    @classmethod
    def parse(self, args: list=None) -> argparse.Namespace:
        return Args().parser.parse_args(args)


@pytest.fixture(scope='module')
def expected():
    return (
        'usage: wwaz [--help] [--verbose]'
        '\n\nRun routine Azure actions.'
        '\n\nGlobal Options:'
        '\n  --help     Show this help message and exit.'
        '\n  --verbose  Whether to output verbose logs. Also whether to show verbose test output. (default: False)\n'
    )

help_args = [pytest.param(['--help'], id='--help')]

@pytest.mark.parametrize('input_args', help_args)
def test_stdout(capsys, expected, input_args):

    capsys.readouterr()
    with pytest.raises(SystemExit):
        Args.parse(input_args)

    actual, _ = capsys.readouterr()
    assert actual == expected

成功的测试运行

假设包含示例代码的文件是~/src/test_argparse.py -

user@computer:~/src$ pytest test_argparse.py -q -s
.
1 passed in 0.00s

未成功的测试运行

假设包含示例代码的文件是~/src/test_argparse.py -

user@computer:~/src$ pytest test_argparse.py -q
F                                                                                                                                                                                            [100%]
============================================================================================= FAILURES =============================================================================================
_______________________________________________________________________________________ test_stdout[--help] ________________________________________________________________________________________

capsys = <_pytest.capture.CaptureFixture object at 0x7f6edf733520>
expected = 'usage: wwaz [--help] [--verbose]\n\nRun routine Azure actions.\n\nGlobal Options:\n  --help     Show this help message and exit.\n  --verbose  Whether to output verbose logs. Also whether to show verbose test output. (default: False)\n'
input_args = ['--help']

    @pytest.mark.parametrize('input_args', help_args)
    def test_stdout(capsys, expected, input_args):
    
        capsys.readouterr()
        with pytest.raises(SystemExit):
            Args.parse(input_args)
    
        actual, _ = capsys.readouterr()
>       assert actual == expected
E       AssertionError: assert 'usage: wwaz ...ult: False)\n' == 'usage: wwaz ...ult: False)\n'
E         Skipping 192 identical leading characters in diff, use -v to show
E         - rbose test output. (default: False)
E         + rbose test
E         +              output. (default: False)

test_argparse.py:51: AssertionError
===================================================================================== short test summary info ======================================================================================
FAILED test_argparse.py::test_stdout[--help] - AssertionError: assert 'usage: wwaz ...ult: False)\n' == 'usage: wwaz ...ult: False)\n'
1 failed in 0.01s
英文:

I'm usingpytest with the capsys fixture to check text output to stdout is as expected.

My issue is that when I run tests using the -s options, the assertions passes. But when I don't use the -s, the assertion fails because some of the text in stdout is wrapped, meaning the actual text is no longer the same as the expected text.

Is there a way of making the capsys fixture capture stdout in the same way as when -s was used every time?

Example code

import argparse
import pytest


class Args:

    class _HelpFormatter(argparse.HelpFormatter):

        def __init__(self, prog: str) -> None:
            super().__init__(prog, max_help_position=50)

    def __init__(self) -> None:

        global_parser = argparse.ArgumentParser(add_help=False, prog='wwaz',
            formatter_class=self._HelpFormatter)
        global_group = global_parser.add_argument_group('Global Options')
        global_group.add_argument('--help', action='help',
            help='Show this help message and exit.')
        global_group.add_argument('--verbose', action='store_true',
            default=False, help='Whether to output verbose logs. Also whether to show verbose test output. (default: False)')

        self.parser = argparse.ArgumentParser(add_help=False, prog='wwaz',
            formatter_class=self._HelpFormatter, parents=[global_parser], 
            description='Run routine Azure actions.')

    @classmethod
    def parse(self, args: list=None) -> argparse.Namespace:
        return Args().parser.parse_args(args)


@pytest.fixture(scope='module')
def expected():
    return (
        'usage: wwaz [--help] [--verbose]'
        '\n\nRun routine Azure actions.'
        '\n\nGlobal Options:'
        '\n  --help     Show this help message and exit.'
        '\n  --verbose  Whether to output verbose logs. Also whether to show verbose test output. (default: False)\n'
    )

help_args = [pytest.param(['--help'], id='--help')]

@pytest.mark.parametrize('input_args', help_args)
def test_stdout(capsys, expected, input_args):

    capsys.readouterr()
    with pytest.raises(SystemExit):
        Args.parse(input_args)

    actual, _ = capsys.readouterr()
    assert actual == expected

Successful test run

Assuming the file containing the example code is ~/src/test_argparse.py -

user@computer:~/src$ pytest test_argparse.py -q -s
.
1 passed in 0.00s

Unsuccessful test run

Assuming the file containing the example code is ~/src/test_argparse.py -

user@computer:~/src$ pytest test_argparse.py -q
F                                                                                                                                                                                            [100%]
============================================================================================= FAILURES =============================================================================================
_______________________________________________________________________________________ test_stdout[--help] ________________________________________________________________________________________

capsys = <_pytest.capture.CaptureFixture object at 0x7f6edf733520>
expected = 'usage: wwaz [--help] [--verbose]\n\nRun routine Azure actions.\n\nGlobal Options:\n  --help     Show this help message and exit.\n  --verbose  Whether to output verbose logs. Also whether to show verbose test output. (default: False)\n'
input_args = ['--help']

    @pytest.mark.parametrize('input_args', help_args)
    def test_stdout(capsys, expected, input_args):
    
        capsys.readouterr()
        with pytest.raises(SystemExit):
            Args.parse(input_args)
    
        actual, _ = capsys.readouterr()
>       assert actual == expected
E       AssertionError: assert 'usage: wwaz ...ult: False)\n' == 'usage: wwaz ...ult: False)\n'
E         Skipping 192 identical leading characters in diff, use -v to show
E         - rbose test output. (default: False)
E         + rbose test
E         +              output. (default: False)

test_argparse.py:51: AssertionError
===================================================================================== short test summary info ======================================================================================
FAILED test_argparse.py::test_stdout[--help] - AssertionError: assert 'usage: wwaz ...ult: False)\n' == 'usage: wwaz ...ult: False)\n'
1 failed in 0.01s

答案1

得分: 0

在与 pytest 社区联系后,他们告诉我,这种行为是因为在使用 -s 时,capsys 会捕获来自终端的 stdout,而在不使用时则会捕获来自非终端输出的 stdout

正如在这个讨论中所解释的 -
> 这是因为 argparse.HelpFormatter 使用 shutil.get_terminal_size() 来获取包装宽度(如果没有给定宽度),当 stdout 不是终端时,它会回退到 80x24。

所以我的解决方案很简单,就是指定我的格式化器的宽度,对于我的用例来说,这足够了。

class _HelpFormatter(argparse.HelpFormatter):

    def __init__(self, prog: str, m=25, w=120) -> None:
        super().__init__(prog, max_help_position=m, width=w)
英文:

After reaching out to the pytest community, I was informed that this behaviour was due to capsys capturing stdout to the terminal when using -s vs capturing it from non-terminal output when not.

As explained in this discussion -
> This is because argparse.HelpFormatter uses shutil.get_terminal_size() to get a wrapping width (if none is given), which then falls back to 80x24 when stdout is not a terminal

So my solution was simply to specify the width of my formatter, which for my use case this is sufficient.

class _HelpFormatter(argparse.HelpFormatter):

    def __init__(self, prog: str, m=25, w=120) -> None:
        super().__init__(prog, max_help_position=m, width=w)

huangapple
  • 本文由 发表于 2023年7月12日 22:25:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76671641.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定