【问题标题】:Pytest capture stdout of a certain testPytest 捕获某个测试的标准输出
【发布时间】:2020-11-12 22:49:43
【问题描述】:

有没有办法只为特定测试获取Captured stdout call 而不会导致测试失败?

假设我有 10 个测试,外加一个 test_summary。 test_summary 实际上只是打印某种测试的摘要/统计信息,但是为了让我获得该输出/打印输出,我目前必须故意使该测试失败。当然,这个 test_summary 最后使用 pytest-ordering 运行。但是有没有更好的方法可以在不通过测试的情况下获得该结果?或者它不应该在测试中,而更多地在 conftest.py 中?请就最佳实践以及如何获得此摘要/结果提供建议(基本上是我编写的特定脚本的打印输出)

【问题讨论】:

  • 您可以使用pytest -s 运行。

标签: python pytest


【解决方案1】:

首先,回答您的确切问题:

有没有办法只为特定测试获取Captured stdout call 而不会导致测试失败?

您可以添加一个模仿Captured stdout call 并在测试成功时打印的自定义部分。每个测试中捕获的输出存储在相关的TestReport 对象中,并通过report.capstdout 访问。示例 impl:将以下代码添加到项目或测试根目录中的 conftest.py 中:

import os

def pytest_terminal_summary(terminalreporter, exitstatus, config):
    # on failures, don't add "Captured stdout call" as pytest does that already
    # otherwise, the section "Captured stdout call" will be added twice
    if exitstatus > 0:
        return
    # get all reports
    reports = terminalreporter.getreports('')
    # combine captured stdout of reports for tests named `<smth>::test_summary`
    content = os.linesep.join(
        report.capstdout for report in reports
        if report.capstdout and report.nodeid.endswith("test_summary")
    )
    # add custom section that mimics pytest's one
    if content:
        terminalreporter.ensure_newline()
        terminalreporter.section(
            'Captured stdout call',
            sep='-',
            blue=True,
            bold=True,
        )
        terminalreporter.line(content)

这将添加一个自定义部分 Captured stdout call,它将仅打印为 ID 以 test_summary 结尾的测试捕获的输出(如果您有多个名为 test_summary 的测试函数,请扩展检查)。为了区分这两个部分,自定义部分有一个蓝色标题;如果您希望它与原始颜色匹配,请通过blue=True arg 删除颜色设置。


现在,解决您的实际问题:

test_summary 实际上只是打印某种测试的摘要/统计数据

对我来说,使用自定义报告测试听起来很像一种解决方法;为什么不在测试中收集数据,然后添加一个自定义部分来打印该数据?要收集数据,您可以例如使用record_property 夹具:

def test_foo(record_property):
    # records a key-value pair
    record_property("hello", "world")


def test_bar(record_property):
    record_property("spam", "eggs")

要收集和输出记录的自定义属性,稍微改变上面的 hookimpl。通过record_property 存储的数据可通过report.user_properties 访问:

import os


def pytest_terminal_summary(terminalreporter, exitstatus, config):
    reports = terminalreporter.getreports('')
    content = os.linesep.join(
        f'{key}: {value}' for report in reports
        for key, value in report.user_properties
    )
    if content:
        terminalreporter.ensure_newline()
        terminalreporter.section(
            'My custom summary',
            sep='-',
            blue=True,
            bold=True
        )
        terminalreporter.line(content)

现在运行上述测试会产生:

$ pytest test_spam.py 
=============================== test session starts ================================
platform linux -- Python 3.9.0, pytest-6.1.2, py-1.9.0, pluggy-0.13.1
rootdir: /home/oleg.hoefling/projects/private/stackoverflow/so-64812992
plugins: metadata-1.10.0, json-report-1.2.4, cov-2.10.1, forked-1.3.0, xdist-2.1.0
collected 2 items                                                                  

test_spam.py ..                                                              [100%]

-------------------------------- My custom summary ---------------------------------
hello: world
spam: eggs
================================ 2 passed in 0.01s =================================

【讨论】:

    【解决方案2】:

    您可以使用标准的 pytest 终端报告器。

    def test_a(request):
        reporter = request.config.pluginmanager.getplugin("terminalreporter")
        reporter.write_line("Hello", yellow=True)
        reporter.write_line("World", red=True)
    

    Reporter 是我记得的所有 pytest 版本的标准插件。不幸的是,虽然 API 相当稳定,但没有记录。

    你可以在pytest的github上找到TerminalReporter类:https://github.com/pytest-dev/pytest/blob/master/src/_pytest/terminal.py

    对你最有用的方法可能是ensure_line()write()flush() 和绝对的冠军——write_line(),它们都可以工作。

    可以在https://github.com/pytest-dev/pytest/blob/master/src/_pytest/_io/terminalwriter.py找到可用的样式

    _esctable = dict(
        black=30,
        red=31,
        green=32,
        yellow=33,
        blue=34,
        purple=35,
        cyan=36,
        white=37,
        Black=40,
        Red=41,
        Green=42,
        Yellow=43,
        Blue=44,
        Purple=45,
        Cyan=46,
        White=47,
        bold=1,
        light=2,
        blink=5,
        invert=7,
    )
    

    小写样式用于前景,大写样式用于背景。

    例如,绿色文字上的黄色应打印为

    reporter.write_line("Text", yellow=True, Green=True)
    

    希望这篇教程能帮到你。

    【讨论】:

    • 非常感谢您提供的信息。对于我的问题,hoefling 的答案更符合我的要求。但我一定会记住这一点,这非常有用,将来也一定会使用这种风格
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 2021-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多