pytest capsys: checking output AND getting it reported?

Deadly 提交于 2020-01-12 13:55:33

问题


Python 3.4.1, pytest 2.6.2.

When a test fails, pytest will routinely report what was printed to stdout by the test. For instance this code:

def method_under_test():
    print("Hallo, Welt!")
    return 41

def test_result_only():
    result = method_under_test()
    assert result == 42

when executed as python -m pytest myfile.py, will report this:

================================== FAILURES ===================================
______________________________ test_result_only _______________________________

    def test_result_only():
        result = method_under_test()
>       assert result == 42
E       assert 41 == 42

pytestest.py:9: AssertionError
---------------------------- Captured stdout call -----------------------------
Hallo, Welt!
========================== 1 failed in 0.03 seconds ===========================

This is a very nice feature. But when I use pytest's built-in capsys fixture, like this:

def test_result_and_stdout(capsys):
    result = method_under_test()
    out, err = capsys.readouterr()
    assert out.startswith("Hello")
    assert result == 42

the report no longer contains the actual output:

================================== FAILURES ===================================
___________________________ test_result_and_stdout ____________________________

capsys = <_pytest.capture.CaptureFixture object at 0x000000000331FB70>

    def test_result_and_stdout(capsys):
        result = method_under_test()
        out, err = capsys.readouterr()
>       assert out.startswith("Hello")
E       assert <built-in method startswith of str object at 0x000000000330C3B0>('Hello')
E        +  where <built-in method startswith of str object at 0x000000000330C3B0> = 'Hallo, Welt!\n'.startswith

pytestest.py:14: AssertionError
========================== 1 failed in 0.03 seconds ===========================

I am not sure whether this behavior is according to specification; the pytest documentation says about readouterr: "After the test function finishes the original streams will be restored."

I have tried assuming capsys is a context manager and have called capsys.__exit__() just before the asserts. This would be an ugly solution, but at least a solution if it restored the output before my assertion. However, this only produces

AttributeError: 'CaptureFixture' object has no attribute '__exit__'

Next I looked into the CaptureFixture class source code and found a promising-looking method close (which calls some pop_outerr_to_orig() method), but calling capsys.close() in my test did not help either, it had no obvious effect at all.

How can I get pytest to report my outputs upon failure in a test using capsys?


回答1:


You're seeing the correct behaviour, when using capsys.readouterr() you're consuming the captured output. Hence any output to stdout and stderr will no longer show up in the test report. But any new output which you create after this and do not consume will still be reported, so you can get the full output back in the report by simply writing it to the output streams once more:

def test_result_and_stdout(capsys):
    result = method_under_test()
    out, err = capsys.readouterr()
    sys.stdout.write(out)
    sys.stderr.write(err)
    assert out.startswith("Hello")
    assert result == 42



回答2:


From the documentation the behavior seems correct: only after the test function (test_result_and_stdout) finishes the output streams will be restored, not after each readouterr call. I don't think that currently capsys supports also redirecting to the original streams besides capturing them, which seems to be what you want.

I would suggest to create an issue in the official repository and see what people have to say.




回答3:


Asides using "startswith".You can also use the "in" keyword, for example:

    assert "Hello" in output

This is great if you have a huge amount of data being passed to stdout, you can use "in" to check for different lines in your stdout.

def test_result_and_stdout(capsys):
    result = method_under_test()
    out, err = capsys.readouterr()
    sys.stdout.write(out)
    sys.stderr.write(err)
    assert "Hello" in out 
    assert result == 42

You can also assert what's passed into stderr instead of stdout by using:

    assert "What you are expecting" in err

Also note that line:

    out, err = capsys.readouterr()

creates a snapshot of the output to both stdout and stderr so far so you can assert what you are expecting for that test.



来源:https://stackoverflow.com/questions/26561822/pytest-capsys-checking-output-and-getting-it-reported

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!