24

According to the pytest documentation, I can assert that a SystemExit() occurs, but I want to do more: I also want to verify the exit code and any messages. I tried the below code, but nothing prints and I'm not sure what I would need to assert to prove that I got the right error code.

    with pytest.raises(SystemExit):
        docopt_args = validate_args(docopt_args)
        out, err = pytest.capsys.readouterr()
        assert out == 'Foo'
        print out, err

When I run my test, it passes, but that is it. Nothing is printed and I don't get an assert error.

The code I expect to be executed is:

        print '\n' + docopt_args['-d'] + ' is not a valid date\n'
        sys.exit(-3)
codeforester
  • 39,467
  • 16
  • 112
  • 140
Robin Siebler
  • 263
  • 1
  • 2
  • 6

2 Answers2

23

This works with the latest pytest:

All you need to do is run pytest with the --capture=sys option and dependent the assertion outside of the raises() context (this bit is important for some reason!)

Example:

#!/usr/bin/env python

from __future__ import print_function

import pytest


def f(code=0):
    print("Foo")
    raise SystemExit(code)


def test_f(capsys):
    with pytest.raises(SystemExit):
        f()
    out, err = capsys.readouterr()
    assert out == "Foo\n"
    print(out, err)

Demo:

$ py.test -v --capture=sys test_foo.py 
======================================= test session starts ========================================
platform linux2 -- Python 2.7.9 -- py-1.4.27 -- pytest-2.7.0 -- /home/prologic/.virtualenvs/test/bin/python
rootdir: /home/prologic/tmp, inifile: 
collected 1 items 

test_foo.py::test_f PASSED

===================================== 1 passed in 0.00 seconds =====================================

Changing the print("Foo") to print("Bar") results in:

$ py.test -v --capture=sys test_foo.py 
======================================= test session starts ========================================
platform linux2 -- Python 2.7.9 -- py-1.4.27 -- pytest-2.7.0 -- /home/prologic/.virtualenvs/test/bin/python
rootdir: /home/prologic/tmp, inifile: 
collected 1 items 

test_foo.py::test_f FAILED

============================================= FAILURES =============================================
______________________________________________ test_f ______________________________________________

capsys = <_pytest.capture.CaptureFixture instance at 0x7f2729405518>

    def test_f(capsys):
        with pytest.raises(SystemExit):
            f()
        out, err = capsys.readouterr()
>       assert out == "Foo\n"
E       assert 'Bar\n' == 'Foo\n'
E         - Bar
E         + Foo

test_foo.py:17: AssertionError
===================================== 1 failed in 0.01 seconds =====================================

Which I think is exactly what you were after!

I did this in a clean virtualenv:

mkvirtualenv test
pip install pytest

The trick here is to read and understand Setting capturing methods or disabling capturing

James Mills
  • 18,669
  • 3
  • 49
  • 62
  • 12
    To make assertions about the exist code you need something like this: "with pytest.raises(SystemExit) as excinfo: assert excinfo.value.code==1" – mvr Jul 26 '16 at 20:16
  • In my case, I didn't try to capture the code that was given (I just needed to test that `SystemExit` was raised) and it worked fine for me. You can verify that it actually works by commenting out your test code and put in like `a = 1` or something like that to see it fail because `SystemExit` wasn't raised. – Sam Dec 14 '16 at 16:05
  • Made [Checking the exit code of a CLI in a test](https://stackoverflow.com/q/57300169/6862601) based on mvr's comment. Surprisingly there wasn't a straight forward post covering this topic. – codeforester Jul 31 '19 at 23:53
6

James Mills' answer doesn't answer the question fully. capsys is the easy bit.

As mentioned in the comment by mvr, to get the exit code returned, do this:

with pytest.raises(SystemExit) as excinfo:
    run_something()

assert excinfo.value.code == 1
mike rodent
  • 14,126
  • 11
  • 103
  • 157