简体   繁体   English

Pytest 捕获某个测试的stdout

[英]Pytest capture stdout of a certain test

Is there a way get the Captured stdout call just for a specific test without failing the test?有没有办法只为特定测试获取Captured stdout call而不会使测试失败?

So lets say I have 10 tests, plus a test_summary.假设我有 10 个测试,外加一个 test_summary。 test_summary really just prints some kind of summary/statistics of the tests, but in order for me to get that output/printout, I have to currently fail that test intentionally. test_summary 实际上只是打印测试的某种摘要/统计数据,但为了让我获得该输出/打印输出,我目前必须故意使该测试失败。 Of course this test_summary run last using pytest-ordering.当然这个 test_summary 最后使用 pytest-ordering 运行。 But is there a better way to get that results without failing the test?但是有没有更好的方法可以在不通过测试的情况下获得结果呢? Or should it not be in a test, but more in the conftest.py or something?或者它不应该在测试中,而是在 conftest.py 或其他东西中? Please advice on the best practice and how I can get this summary/results (basically a printout from a specific script I wrote)请就最佳实践以及我如何获得此摘要/结果提出建议(基本上是我编写的特定脚本的打印输出)

First, to answer your exact question:首先,回答你的确切问题:

Is there a way get the Captured stdout call just for a specific test without failing the test?有没有办法只为特定测试获取Captured stdout call而不会使测试失败?

You can add a custom section that mimics Captured stdout call and is printed on test success.您可以添加一个自定义部分,模仿Captured stdout call并在测试成功时打印。 The output captured in each test is stored in the related TestReport object and is accessed via report.capstdout .在每个测试中捕获的 output 存储在相关的TestReport object 中,并通过report.capstdout访问。 Example impl: add the following code to a conftest.py in your project or tests root directory:示例实现:将以下代码添加到项目或测试根目录中的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)

This will add a custom section Captured stdout call that will only print the output captured for the test whose ID ends with test_summary (if you have multiple test functions named test_summary , extend the check).这将添加一个自定义部分Captured stdout call ,该调用将只打印为 ID 以 test_summary 结尾的测试捕获的test_summary (如果您有多个名为test_summary的测试函数,请扩展检查)。 To distinct both sections, the custom one has a blue header;为了区分这两个部分,自定义部分有一个蓝色的 header; if you want it to match the original, remove color setting via blue=True arg.如果您希望它与原始颜色匹配,请通过blue=True arg 删除颜色设置。


Now, to address your actual problem:现在,解决您的实际问题:

test_summary really just prints some kind of summary/statistics of the tests test_summary实际上只是打印测试的某种摘要/统计信息

Using a test for custom reporting smells a lot like a workaround to me;使用自定义报告测试对我来说很像是一种变通方法; why not collect the data in the tests and add a custom section printing that data afterwards?为什么不在测试中收集数据并在之后添加一个自定义部分来打印该数据? To collect the data, you can eg use the record_property fixture:要收集数据,您可以使用record_property fixture:

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


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

To collect and output the custom properties recorded, slightly alter the above hookimpl.收集和 output 记录的自定义属性,稍微改变上面的 hookimpl。 The data stored via record_property is accessible via report.user_properties :通过report.user_properties record_property

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)

Running the above tests now yields:运行上述测试现在产生:

$ 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 =================================

You can use the standard pytest terminal reporter.您可以使用标准的 pytest 终端记者。

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

The reporter is the standard plugin available for all pytest versions that I can recall. reporter 是我记得的所有 pytest 版本的标准插件。 It is not documented unfortunately while the API is pretty stable.不幸的是,它没有记录在案,而 API 非常稳定。

You can find TerminalReporter class on pytest's github: https://github.com/pytest-dev/pytest/blob/master/src/_pytest/terminal.py你可以在pytest的github上找到TerminalReporter class: https://github.com/pytest-dev/pytest/blob/master/src/_pytest/terminal.py

The most useful for you methods are probably ensure_line() , write() , flush() and the absolute champion -- write_line() that just do all work.对您最有用的方法可能是ensure_line()write()flush()和绝对冠军 -- write_line() ,它们可以完成所有工作。

Available styles could be found at https://github.com/pytest-dev/pytest/blob/master/src/_pytest/_io/terminalwriter.py可用的 styles 可以在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,
)

Lowercased styles are for the foreground, capitalized are for background.小写 styles 表示前景,大写表示背景。

For example, the yellow on the green text should be printed as例如,绿色文本上的黄色应打印为

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

I hope this instruction can help.我希望这个说明能有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM