2

Is there a way get the Captured stdout call just for a specific test without failing the test?

So lets say I have 10 tests, plus a 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. Of course this test_summary run last using 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? Please advice on the best practice and how I can get this summary/results (basically a printout from a specific script I wrote)

1
  • 1
    You could run with pytest -s. Commented Nov 12, 2020 at 22:57

2 Answers 2

4

First, to answer your exact question:

Is there a way get the Captured stdout call just for a specific test without failing the test?

You can add a custom section that mimics Captured stdout call and is printed on test success. The output captured in each test is stored in the related TestReport object and is accessed via report.capstdout. Example impl: add the following code to a conftest.py in your project or tests root directory:

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). To distinct both sections, the custom one has a blue header; if you want it to match the original, remove color setting via blue=True arg.


Now, to address your actual problem:

test_summary really just prints some kind of summary/statistics of the tests

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 e.g. use the 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. The data stored via record_property is accessible via 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) 

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 ================================= 
Sign up to request clarification or add additional context in comments.

Comments

1

You can use the standard pytest terminal reporter.

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. It is not documented unfortunately while the API is pretty stable.

You can find TerminalReporter class on pytest's github: 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.

Available styles could be found at 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.

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.

1 Comment

Thanks alot for the info. For my problem, hoefling's answer is more what I am looking for. But I will definitely keep this in mind, this is very useful and will definitely use this style in the future as well

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.