631

有时我只想在我的代码中插入一些打印语句,然后看看我在执行它时会打印出什么。我常用的“锻炼”方法是使用现有的 pytest 测试。但是当我运行这些时,我似乎看不到任何标准输出(至少在我的 IDE PyCharm 中)。

有没有一种简单的方法可以在 pytest 运行期间查看标准输出?

4

12 回答 12

817

-s开关禁用每次测试捕获(仅当测试失败时)。

-s相当于--capture=no

于 2013-01-19T12:22:57.527 回答
175

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.

于 2019-12-03T12:03:38.613 回答
60

在对已接受答案的赞成评论中,问道:

有什么方法可以打印到控制台捕获输出以便它显示在 junit 报告中?

在 UNIX 中,这通常称为teeing。理想情况下, py.test 的默认设置是发球而不是捕获。非理想情况下,py.test 和任何现有的第三方 py.test 插件(我知道,无论如何)都不支持 teeing——尽管 Python 微不足道地支持 teeing out-of-the-box

Monkey-patching py.test 做任何不受支持的事情都是不平凡的。为什么?因为:

  • 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.

于 2016-06-26T05:58:13.403 回答
53

运行测试时使用该-s选项。运行测试时,所有打印语句都exampletest.py将打印在控制台上。

py.test exampletest.py -s
于 2016-02-04T17:26:38.360 回答
37

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')
于 2017-01-08T13:38:22.430 回答
24

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

于 2020-04-10T22:09:15.310 回答
22

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

-v it's a short --verbose

-s means 'disable all capturing'



于 2017-12-14T15:01:07.523 回答
8

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
于 2020-06-22T13:31:44.260 回答
6
pytest test_name.py -v -s

Simple!

于 2020-10-01T13:25:23.170 回答
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.

于 2019-02-06T03:47:38.127 回答
3

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
于 2021-03-08T14:27:22.073 回答
3

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/

于 2021-05-05T02:01:22.317 回答