How to see normal print output created during pytest run?
Asked Answered
A

18

882

Sometimes I want to just insert some print statements in my code, and see what gets printed out when I exercise it. My usual way to "exercise" it is with existing pytest tests. But when I run these, I don't seem able to see any standard output (at least from within PyCharm, my IDE).

Is there a simple way to see standard output during a pytest run?

Ashelman answered 18/1, 2013 at 18:14 Comment(0)
L
1073

The -s switch disables per-test capturing (only if a test fails).

-s is equivalent to --capture=no.

Lumen answered 19/1, 2013 at 12:22 Comment(6)
This causes the output to be interleaved with the Pytest output. What you probably want is for Pytest to capture the output as usual and display it when tests pass as well as fail. See https://mcmap.net/q/53518/-how-to-see-normal-print-output-created-during-pytest-runBioclimatology
How to see the prints during the run without waiting for the end?Chainey
If using logging, see also https://mcmap.net/q/54781/-logging-within-pytest-tests for additional options to use with -sRosena
I would like to know what you mean by 'per-test capturing'. I'm guessing, disabling capturing is effectively the same as enabling stdout, but I'm not sure since I don't know the ins and outs of pytest - I would just like to see my print statements. Would you mind to elaborate?Nagey
This does not work, neither do any of the --capture options. The only option that seems to work is "-o log_cli=True". But, it's entirely possible that the meaning of -s has changed since 2013.Rid
-s applies to all tests, not just failing tests. (It doesn't capture the output at all, it doesn't even know whether the test passed or failed at that point)Armelda
D
402

pytest captures the stdout from individual tests and displays them only on certain conditions, along with the summary of the tests it prints by default.

Extra summary info can be shown using the '-r' option:

pytest -rP

shows the captured output of passed tests.

pytest -rx

shows the captured output of failed tests (default behaviour).

The formatting of the output is prettier with -r than with -s.

Deci answered 3/12, 2019 at 12:3 Comment(8)
This is the actual answer I was looking for! Thank you. (Having the stdout come AFTER the test results is desired. When they are interleaved, the printed lines lose value.)Hireling
How to do this in Pycharm (not via command line)?Chainey
@Chainey per this question, you can do: Edit Configurations > Additional Parameters > -r P. I tested this on my side with Pycharm 2021.1 successfully. Meaning in add'l params, you enter -r with a space then P. Hope that helpsBurtburta
@Burtburta this is command line via pycharm. I was looking for something built inChainey
pytest -rA should print all outputs. docs.pytest.org/en/6.2.x/…Succinctorium
Note the @Succinctorium comment is the best one. Especially if you are working through a number of tests and every time want to see output for fail or pass for that particular test -rA does it. This is the easy one size fits all answer.Nubile
This also lets you see the output from print statements added to the test directly. Helped me when I was debugging a failing test.Impeccable
Note that if a message is passed to assert, and not printed to stdout, PyCharm doesn't show it due to this bug. So, assert False, "Hello" will never print Hello when the test fails.Yuille
B
73

When running the test use the -s option. All print statements in exampletest.py would get printed on the console when test is run.

py.test exampletest.py -s
Bracy answered 4/2, 2016 at 17:26 Comment(0)
P
62

In an upvoted comment to the accepted answer, Joe asks:

Is there any way to print to the console AND capture the output so that it shows in the junit report?

In UNIX, this is commonly referred to as teeing. Ideally, teeing rather than capturing would be the py.test default. Non-ideally, neither py.test nor any existing third-party py.test plugin (...that I know of, anyway) supports teeing – despite Python trivially supporting teeing out-of-the-box.

Monkey-patching py.test to do anything unsupported is non-trivial. Why? Because:

  • Most py.test functionality is locked behind a private _pytest package not intended to be externally imported. Attempting to do so without knowing what you're doing typically results in the public pytest package raising obscure exceptions at runtime. Thanks alot, py.test. Really robust architecture you got there.
  • Even when you do figure out how to monkey-patch the private _pytest API in a safe manner, you have to do so before running the public pytest package run by the external py.test command. You cannot do this in a plugin (e.g., a top-level conftest module in your test suite). By the time py.test lazily gets around to dynamically importing your plugin, any py.test class you wanted to monkey-patch has long since been instantiated – and you do not have access to that instance. This implies that, if you want your monkey-patch to be meaningfully applied, you can no longer safely run the external py.test command. Instead, you have to wrap the running of that command with a custom setuptools test command that (in order):
    1. Monkey-patches the private _pytest API.
    2. Calls the public pytest.main() function to run the py.test command.

This answer monkey-patches py.test's -s and --capture=no options to capture stderr but not stdout. By default, these options capture neither stderr nor stdout. This isn't quite teeing, of course. But every great journey begins with a tedious prequel everyone forgets in five years.

Why do this? I shall now tell you. My py.test-driven test suite contains slow functional tests. Displaying the stdout of these tests is helpful and reassuring, preventing leycec from reaching for killall -9 py.test when yet another long-running functional test fails to do anything for weeks on end. Displaying the stderr of these tests, however, prevents py.test from reporting exception tracebacks on test failures. Which is completely unhelpful. Hence, we coerce py.test to capture stderr but not stdout.

Before we get to it, this answer assumes you already have a custom setuptools test command invoking py.test. If you don't, see the Manual Integration subsection of py.test's well-written Good Practices page.

Do not install pytest-runner, a third-party setuptools plugin providing a custom setuptools test command also invoking py.test. If pytest-runner is already installed, you'll probably need to uninstall that pip3 package and then adopt the manual approach linked to above.

Assuming you followed the instructions in Manual Integration highlighted above, your codebase should now contain a PyTest.run_tests() method. Modify this method to resemble:

class PyTest(TestCommand):
             .
             .
             .
    def run_tests(self):
        # Import the public "pytest" package *BEFORE* the private "_pytest"
        # package. While importation order is typically ignorable, imports can
        # technically have side effects. Tragicomically, that is the case here.
        # Importing the public "pytest" package establishes runtime
        # configuration required by submodules of the private "_pytest" package.
        # The former *MUST* always be imported before the latter. Failing to do
        # so raises obtuse exceptions at runtime... which is bad.
        import pytest
        from _pytest.capture import CaptureManager, FDCapture, MultiCapture

        # If the private method to be monkey-patched no longer exists, py.test
        # is either broken or unsupported. In either case, raise an exception.
        if not hasattr(CaptureManager, '_getcapture'):
            from distutils.errors import DistutilsClassError
            raise DistutilsClassError(
                'Class "pytest.capture.CaptureManager" method _getcapture() '
                'not found. The current version of py.test is either '
                'broken (unlikely) or unsupported (likely).'
            )

        # Old method to be monkey-patched.
        _getcapture_old = CaptureManager._getcapture

        # New method applying this monkey-patch. Note the use of:
        #
        # * "out=False", *NOT* capturing stdout.
        # * "err=True", capturing stderr.
        def _getcapture_new(self, method):
            if method == "no":
                return MultiCapture(
                    out=False, err=True, in_=False, Capture=FDCapture)
            else:
                return _getcapture_old(self, method)

        # Replace the old with the new method.
        CaptureManager._getcapture = _getcapture_new

        # Run py.test with all passed arguments.
        errno = pytest.main(self.pytest_args)
        sys.exit(errno)

To enable this monkey-patch, run py.test as follows:

python setup.py test -a "-s"

Stderr but not stdout will now be captured. Nifty!

Extending the above monkey-patch to tee stdout and stderr is left as an exercise to the reader with a barrel-full of free time.

Piero answered 26/6, 2016 at 5:58 Comment(2)
pytest has teeing with --capture=tee-sys docs.pytest.org/en/stable/…Footplate
I think this is the first SO answer that made me laugh AND was relevant. Thank you :)Knowing
H
46

pytest --capture=tee-sys was recently added (v5.4.0). You can capture as well as see the output on stdout/err.

Hyehyena answered 10/4, 2020 at 22:9 Comment(0)
E
42

According to pytest documentation, version 3 of pytest can temporary disable capture in a test:

def test_disabling_capturing(capsys):
    print('this output is captured')
    with capsys.disabled():
        print('output not captured, going directly to sys.stdout')
    print('this output is also captured')
Embassy answered 8/1, 2017 at 13:38 Comment(0)
C
24

Try pytest -s -v test_login.py for more info in console.

-v it's a short --verbose

-s means 'disable all capturing'



Caoutchouc answered 14/12, 2017 at 15:1 Comment(2)
if you are using pytest.ini file you can use: addopts = -s -v python_files = test_login.pyBetts
If you are trying to get the print even if a test passes, this was the answer I needed.Damales
H
12

You can also enable live-logging by setting the following in pytest.ini or tox.ini in your project root.

[pytest]
log_cli = True

Or specify it directly on cli

pytest -o log_cli=True
Hereabout answered 22/6, 2020 at 13:31 Comment(3)
Tested it on pytest-5.3.5 and it works. You may want to add -s flag too. pytest -s -o log_cli=TrueHereabout
This is the only answer so far that actually works. -s and --capture both do not work.Rid
Yeah when your test is timeouting AND doesn't respond to CTRL-C debugging gets really difficult without live logs. Thanks for this answer!Tremble
I
11
pytest test_name.py -v -s

Simple!

Isador answered 1/10, 2020 at 13:25 Comment(0)
P
7

You can show print output with these commands below. *-rP can more clearly show print output than -s, --capture=no and --capture=tee-sys:

pytest -rP
pytest -s
pytest --capture=no
pytest --capture=tee-sys
Plauen answered 8/8, 2023 at 1:59 Comment(1)
What do you mean by "more clear"? I find pytest --capture=no very clear because it’s explicit, while -rP is pretty obscure.Delative
D
5

I would suggest using -h command. There're quite interesting commands might be used for. but, for this particular case: -s shortcut for --capture=no. is enough

pytest <test_file.py> -s
Direful answered 8/3, 2021 at 14:27 Comment(0)
R
5

If you are using logging, you need to specify to turn on logging output in addition to -s for generic stdout. Based on Logging within pytest tests, I am using:

pytest --log-cli-level=DEBUG -s my_directory/

Rosena answered 5/5, 2021 at 2:1 Comment(0)
H
4

If you are using PyCharm IDE, then you can run that individual test or all tests using Run toolbar. The Run tool window displays output generated by your application and you can see all the print statements in there as part of test output.

Henshaw answered 6/2, 2019 at 3:47 Comment(1)
Do you know how to make PyCharm print while the test is running? (instead of after the test has passed)Songster
B
3

If anyone wants to run tests from code with output:

if __name__ == '__main__':
    pytest.main(['--capture=no'])
Bullshit answered 1/4, 2022 at 21:9 Comment(0)
H
1

If using pytest-xdist for running tests using multiple parallel processes (via pytest -n4 -s, for example), then the -s won't be enough to see your print() calls.

If you want to print something while using pytest-xdist, this WON'T work: print(message)

Do THIS instead if you want to see output during pytest-xdist parallel tests:

import sys
sys.stderr.write(message + "\n")

This is based on https://mcmap.net/q/54783/-why-does-pytest-xdist-not-capture-output - that solution also suggests putting sys.stdout = sys.stderr into a conftest.py file so that all print() calls display their output when using pytest -n4 -s --log-cli-level=debug.

Hypotenuse answered 27/6, 2023 at 23:20 Comment(0)
V
1

A simple solution is to log output in real time. The other answers here seem not to log stuff immediately.

$ pytest -o log_cli=true

Viewer answered 6/12, 2023 at 6:16 Comment(0)
L
0

The capsys, capsysbinary, capfd, and capfdbinary fixtures allow access to stdout/stderr output created during test execution.

Here is an example test function that performs some output related checks:

def test_print_something_even_if_the_test_pass(self, capsys):
    text_to_be_printed = "Print me when the test pass."
    print(text_to_be_printed)
    p_t = capsys.readouterr()
    sys.stdout.write(p_t.out)
    # the two rows above will print the text even if the test pass.

Here is the result:

test_print_something_even_if_the_test_pass PASSED [100%]Print me when the test pass.
Linalool answered 1/6, 2022 at 14:37 Comment(0)
B
0

pytest <test_file.py> -s -> is the best answer for this question. If you're looking to see the values of any variable, or outcome of any function and or anything similar while running pytest then what all you have to do is to put the print statement within the function that you'll assert and run this command.

Bennion answered 25/5, 2023 at 8:0 Comment(1)
Please don't repeat existing answers. Instead, vote up the answers that you find helpful. - From ReviewAlarice

© 2022 - 2024 — McMap. All rights reserved.