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

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


当前回答

在一个被认可的答案的好评评论中,乔问道:

是否有办法将输出打印到控制台并捕获输出,以便在junit报告中显示?

在UNIX中,这通常称为tee。理想情况下,py应该是tee(发球)而不是capturing(捕捉)。测试默认。非理想情况下,都不是py。测试或任何现有的第三方py。测试插件(…(至少我知道)支持开箱即用——尽管Python简单地支持开箱即用。

猴子补丁py。测试做任何不受支持的事情是很重要的。为什么?因为:

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): Monkey-patches the private _pytest API. Calls the public pytest.main() function to run the py.test command.

这个答案是monkey-patches py。Test的-s和——capture=没有捕获标准错误但不捕获标准输出的选项。默认情况下,这些选项既不捕获stderr也不捕获stdout。当然,这还不完全是开球。但每一次伟大的旅程都始于一个乏味的前传,五年后每个人都忘记了。

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.

在我们开始之前,这个答案假设您已经有一个调用py.test的自定义setuptools测试命令。如果没有,请参阅py的手动集成小节。测试的良好实践页面。

不要安装pytest-runner,这是一个第三方setuptools插件,提供了一个自定义setuptools测试命令,也调用了py.test。如果已经安装了pytest-runner,则可能需要卸载pip3包,然后采用上面链接的手动方法。

假设您遵循了上面突出显示的手动集成中的说明,那么您的代码库现在应该包含一个PyTest.run_tests()方法。修改此方法如下:

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)

要启用这个monkey-patch,请执行py命令。测试如下:

python setup.py test -a "-s"

Stderr而不是stdout现在将被捕获。漂亮的!

将上面的monkey-patch扩展到tee stdout和stderr,留给有大量空闲时间的读者做练习。

其他回答

如果使用PyCharm IDE,则可以使用运行工具栏运行单个测试或所有测试。Run工具窗口显示应用程序生成的输出,您可以在其中看到作为测试输出的一部分的所有打印语句。

根据pytest文档,版本3的pytest可以在测试中临时禁用捕获:

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')

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

-s等价于——capture=no。

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

py.test exampletest.py -s

您还可以通过在项目根目录中的pytest.ini或tox.ini中设置以下内容来启用实时日志记录。

[pytest]
log_cli = True

或者直接在cli中指定

pytest -o log_cli=True