在pytest中如何将内容打印到控制台?

526

pytest在我使用print时不会打印到控制台。文档似乎说默认情况下应该可以工作。

我正在使用pytest my_tests.py来运行这个测试:

import myapplication as tum

class TestBlogger:
    @classmethod
    def setup_class(self):
        self.user = "alice"
        self.b = tum.Blogger(self.user)
        print "This should be printed, but it won't be!"

    def test_inherit(self):
        assert issubclass(tum.Blogger, tum.Site)
        links = self.b.get_links(posts)
        print len(links)   # This won't print either.

我的标准输出控制台没有打印任何内容(只有正常的进度以及测试通过/失败的数量)。

而我正在测试的脚本中包含了打印语句:

class Blogger(Site):
    get_links(self, posts):
        print len(posts)   # It won't get printed in the test.

unittest模块中,默认情况下会打印所有内容,这正是我所需要的。然而,出于其他原因,我希望使用pytest有人知道如何显示打印语句吗?

1
也许stdout被覆盖了。如果你使用sys.stdout.write("Test")会发生什么?sys.__stdout__.write("Test")呢?后者应该总是写入系统定义的stdout,也就是控制台。如果这两个命令执行不同的操作,则stdout已经被更改;如果它们执行相同的操作,则问题在于其他方面。 - TheSoundDefense
12个回答

476

默认情况下,py.test会捕获标准输出的结果,以便控制打印输出的方式。如果不这样做,它将输出大量文本,但没有上下文说明是哪个测试打印了这些文本。

然而,如果一个测试失败,它将在生成的报告中包括一个部分,显示在该特定测试中打印到标准输出的内容。

例如:

def test_good():
    for i in range(1000):
        print(i)

def test_bad():
    print('this should fail!')
    assert False

以下输出结果:

>>> py.test tmp.py
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py .F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
------------------------------- Captured stdout --------------------------------
this should fail!
====================== 1 failed, 1 passed in 0.04 seconds ======================

注意 Captured stdout 部分。

如果您想要在执行时查看print 语句,可以向py.test传递-s标志。但是,请注意有时这可能很难解析。

>>> py.test tmp.py -s
============================= test session starts ==============================
platform darwin -- Python 2.7.6 -- py-1.4.20 -- pytest-2.5.2
plugins: cache, cov, pep8, xdist
collected 2 items

tmp.py 0
1
2
3
... and so on ...
997
998
999
.this should fail!
F

=================================== FAILURES ===================================
___________________________________ test_bad ___________________________________

    def test_bad():
        print('this should fail!')
>       assert False
E       assert False

tmp.py:7: AssertionError
====================== 1 failed, 1 passed in 0.02 seconds ======================

39
嗯... 仍然没有记录我的打印语句。 - Tim Boland
2
对我来说不起作用。 - FiftiN
添加 -v--verbose 标志对我很有帮助。不确定其他人是否没有它会怎样。 - Denis

417

使用-s选项:

pytest -s

详细答案

根据文档

在测试执行期间,任何发送到 stdoutstderr 的输出都将被捕获。如果测试或设置方法失败,则通常会显示相应的捕获输出以及失败的回溯跟踪。

pytest 有一个选项 --capture=method,其中 method 是每个测试捕获方法,可以是以下之一:fdsysnopytest 还有一个选项 -s,它是 --capture=no 的快捷方式,这是一个允许你在控制台中看到打印语句的选项。

pytest --capture=no     # show print statements in console
pytest -s               # equivalent to previous command

设置捕获方法或禁用捕获

pytest 有两种执行捕获的方式:

  1. 文件描述符(FD)级别捕获(默认):操作系统文件描述符1和2上的所有写入都将被捕获。

  2. 系统级别捕获:仅会捕获对Python文件sys.stdout和sys.stderr的写入。不会执行对文件描述符的写入捕获。

pytest -s            # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd  # also point filedescriptors 1 and 2 to temp file

1
你不会也知道如何显示日志语句吧?比如 logger.warning?我刚刚明白了:--log-cli-level=WARNING 将显示所有 WARNING 级别的日志。 - smac89

115

使用-s选项会打印出所有函数的输出,可能太多了。

如果您需要特定的输出,您提到的文档页面提供了几个建议:

  1. 在您的函数末尾插入assert False,"dumb assert to make PyTest print my stuff",您将由于失败的测试而看到您的输出。

  2. PyTest会向您传递一个特殊的对象,您可以将输出写入文件以便稍后检查,例如:

    def test_good1(capsys):
        for i in range(5):
            print i
        out, err = capsys.readouterr()
        open("err.txt", "w").write(err)
        open("out.txt", "w").write(out)
    

    你可以在单独的标签页中打开 outerr 文件,并让编辑器自动刷新它们,或者使用简单的 py.test; cat out.txt shell 命令来运行你的测试。

这是一种相当投机的方法,但也许正是你需要的方法:毕竟,TDD 意味着你要与代码交互并在完成后将其保持干净和无声 :-).


我尝试了版本1,使用pytest 3.8.1。 不幸的是,它只打印了测试函数块,而没有打印print语句的输出 :( 还有其他的技巧吗? - U.V.
@U.V. - 与其使用print()函数,你应该将你想要打印的变量或信息放在断言语句中逗号__后面__。例如,assert False, what_are_you会在pytest报告中“打印出”what_are_you的值。 - Mart Van de Ven

56

这是我知道的最干净的打印单个语句到sys.stdout的方法(不用人为地使您的测试失败或启用-s选项)—— 您可以看到您想要的具体输出,而且仅有这些:

  1. 将内置参数capsys添加到您的测试函数中。(这意味着您将capsys添加到参数列表中,例如:
def test_function(existing_parameters, capsys):
  • 在您的代码中,只需插入:
  • with capsys.disabled():
       print("this output will not be captured and go straight to sys.stdout")
    

    请参阅https://buildmedia.readthedocs.org/media/pdf/pytest/latest/pytest.pdf(2.11 如何捕获stdout/stderr输出)。


    4
    这应该是首选答案!它似乎可以完美地运行而不会有任何副作用。 - pabouk - Ukraine stay strong
    2
    只有这样我才能看到我的打印日志。我没有通过-s选项或创建文件看到打印日志。 - Karimai
    编辑队列已满,无法编辑:实际上只是 def test_something(capsys): ...。请参见 https://docs.pytest.org/en/6.2.x/fixture.html。 - CodePrinz
    这在会话范围的装置上不起作用,https://github.com/pytest-dev/pytest/issues/2704。 - undefined

    20

    PyTest完全关闭所有输出时,我需要打印有关跳过测试的重要警告。

    我不想因为发送信号而使测试失败,所以我执行了以下hack:

    def test_2_YellAboutBrokenAndMutedTests():
        import atexit
        def report():
            print C_patch.tidy_text("""
    In silent mode PyTest breaks low level stream structure I work with, so
    I cannot test if my functionality work fine. I skipped corresponding tests.
    Run `py.test -s` to make sure everything is tested.""")
        if sys.stdout != sys.__stdout__:
            atexit.register(report)
    

    atexit 模块允许我在 PyTest 释放输出流后打印一些东西。输出如下所示:

    ============================= test session starts ==============================
    platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
    rootdir: /media/Storage/henaro/smyth/Alchemist2-git/sources/C_patch, inifile: 
    collected 15 items 
    
    test_C_patch.py .....ssss....s.
    
    ===================== 10 passed, 5 skipped in 0.15 seconds =====================
    In silent mode PyTest breaks low level stream structure I work with, so
    I cannot test if my functionality work fine. I skipped corresponding tests.
    Run `py.test -s` to make sure everything is tested.
    ~/.../sources/C_patch$
    

    即使在 PyTest 处于静默模式时,该消息也会被打印出来,并且如果您使用py.test -s运行程序,则不会打印该消息,因此一切都已经得到很好的测试。


    2
    完美用于输出自定义测试指标。 - z0r

    10

    根据 pytest 文档pytest --capture=sys 应该可以工作。如果您想在测试中捕获标准输出,请参考 capsys fixture。


    当需要在终端打印变量时,这对我很有效... - Sukma Saputra
    2
    要在每次运行pytest时传递--capture选项,请在文件pytest.ini[pytest]部分中添加以下行:addopts = --capture=tee-sys文档)。 - 0 _
    对于 pytest 7.0.1,它显示 pytest: error: unrecognized arguments: --capture=sys - Smile

    4
    你也可以通过Pycharm GUI设置:转到运行>编辑配置。在那里,选择想要启用打印语句的测试,并将-s添加到其他参数字段中。
    我这样做是因为虽然我主要使用Pycharm调试器来调试pytest函数(即通过GUI),但我的特定用例还需要我知道代码中其他地方正在发生什么,打印语句对于这一点非常有用。

    4

    我最初进来是为了找到如何使PyTest在从VSCode中运行/调试单元测试时在控制台中打印信息。 可以使用以下launch.json配置来实现此目标。 假设.venv是虚拟环境文件夹。

        "version": "0.2.0",
        "configurations": [
            {
                "name": "PyTest",
                "type": "python",
                "request": "launch",
                "stopOnEntry": false,
                "pythonPath": "${config:python.pythonPath}",
                "module": "pytest",
                "args": [
                    "-sv"
                ],
                "cwd": "${workspaceRoot}",
                "env": {},
                "envFile": "${workspaceRoot}/.venv",
                "debugOptions": [
                    "WaitOnAbnormalExit",
                    "WaitOnNormalExit",
                    "RedirectOutput"
                ]
            }
        ]
    }
    

    3

    不需要人为地让您的测试失败或启用-s选项

    import warnings
    
    text = "asdf" 
    
    warnings.warn(UserWarning("{}".format(text)))
    

    font:https://docs.pytest.org/en/stable/how-to/capture-warnings.html - Felipe Berretella
    非常好,完成了任务。我想在某种情况下,在运行开始时打印一次警告(确实)。这样做的明显位置是conftest.py文件中的pytest_configure方法。但是在那个时候,例如,pytest还没有配置其记录器(而且它们也不会有太大帮助)。 - mike rodent

    2

    我通常使用这个命令:

    pytest -v -rs --html=report.html --self-contained-html test_Suite.py -s
    

    上述命令还会生成一个report.html文件,其中捕获并保存了所有的打印语句。而末尾的-s将在终端中显示打印语句。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接