Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capsysbinary fixture #2935

Merged
merged 1 commit into from Nov 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 28 additions & 5 deletions _pytest/capture.py
Expand Up @@ -180,7 +180,7 @@ def suspend_capture_item(self, item, when, in_=False):
item.add_report_section(when, "stderr", err)


capture_fixtures = {'capfd', 'capfdbinary', 'capsys'}
capture_fixtures = {'capfd', 'capfdbinary', 'capsys', 'capsysbinary'}


def _ensure_only_one_capture_fixture(request, name):
Expand All @@ -207,6 +207,22 @@ def capsys(request):
yield fixture


@pytest.fixture
def capsysbinary(request):
"""Enable capturing of writes to sys.stdout/sys.stderr and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple. ``out`` and ``err`` will be ``bytes``
objects.
"""
_ensure_only_one_capture_fixture(request, 'capsysbinary')
# Currently, the implementation uses the python3 specific `.buffer`
# property of CaptureIO.
if sys.version_info < (3,):
raise request.raiseerror('capsysbinary is only supported on python 3')
with _install_capture_fixture_on_item(request, SysCaptureBinary) as fixture:
yield fixture


@pytest.fixture
def capfd(request):
"""Enable capturing of writes to file descriptors 1 and 2 and make
Expand Down Expand Up @@ -506,10 +522,9 @@ def start(self):
setattr(sys, self.name, self.tmpfile)

def snap(self):
f = self.tmpfile
res = f.getvalue()
f.truncate(0)
f.seek(0)
res = self.tmpfile.getvalue()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res

def done(self):
Expand All @@ -528,6 +543,14 @@ def writeorg(self, data):
self._old.flush()


class SysCaptureBinary(SysCapture):
def snap(self):
res = self.tmpfile.buffer.getvalue()
self.tmpfile.seek(0)
self.tmpfile.truncate()
return res


class DontReadFromInput:
"""Temporary stub class. Ideally when stdin is accessed, the
capturing should be turned off, with possibly all data captured
Expand Down
2 changes: 2 additions & 0 deletions changelog/2934.feature
@@ -0,0 +1,2 @@
Add ``capsysbinary`` a version of ``capsys`` which returns bytes from
``readouterr()``.
17 changes: 13 additions & 4 deletions doc/en/capture.rst
Expand Up @@ -85,9 +85,9 @@ of the failing function and hide the other one::
Accessing captured output from a test function
---------------------------------------------------

The ``capsys``, ``capfd``, and ``capfdbinary`` fixtures allow access to
stdout/stderr output created during test execution. Here is an example test
function that performs some output related checks:
The ``capsys``, ``capsysbinary``, ``capfd``, and ``capfdbinary`` fixtures
allow access to stdout/stderr output created during test execution. Here is
an example test function that performs some output related checks:

.. code-block:: python

Expand Down Expand Up @@ -115,11 +115,20 @@ same interface but allows to also capture output from
libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).

.. versionadded:: 3.3

If the code under test writes non-textual data, you can capture this using
the ``capsysbinary`` fixture which instead returns ``bytes`` from
the ``readouterr`` method. The ``capfsysbinary`` fixture is currently only
available in python 3.


.. versionadded:: 3.3

If the code under test writes non-textual data, you can capture this using
the ``capfdbinary`` fixture which instead returns ``bytes`` from
the ``readouterr`` method.
the ``readouterr`` method. The ``capfdbinary`` fixture operates on the
filedescriptor level.


.. versionadded:: 3.0
Expand Down
34 changes: 33 additions & 1 deletion testing/test_capture.py
Expand Up @@ -470,6 +470,38 @@ def test_hello(capfdbinary):
""")
reprec.assertoutcome(passed=1)

@pytest.mark.skipif(
sys.version_info < (3,),
reason='only have capsysbinary in python 3',
)
def test_capsysbinary(self, testdir):
reprec = testdir.inline_runsource("""
def test_hello(capsysbinary):
import sys
# some likely un-decodable bytes
sys.stdout.buffer.write(b'\\xfe\\x98\\x20')
blueyed marked this conversation as resolved.
Show resolved Hide resolved
out, err = capsysbinary.readouterr()
assert out == b'\\xfe\\x98\\x20'
assert err == b''
""")
reprec.assertoutcome(passed=1)

@pytest.mark.skipif(
sys.version_info >= (3,),
reason='only have capsysbinary in python 3',
)
def test_capsysbinary_forbidden_in_python2(self, testdir):
testdir.makepyfile("""
def test_hello(capsysbinary):
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines([
"*test_hello*",
"*capsysbinary is only supported on python 3*",
"*1 error in*",
])

def test_partial_setup_failure(self, testdir):
p = testdir.makepyfile("""
def test_hello(capsys, missingarg):
Expand Down Expand Up @@ -1233,7 +1265,7 @@ def test_capattr():
reprec.assertoutcome(passed=1)


def test_pickling_and_unpickling_enocded_file():
def test_pickling_and_unpickling_encoded_file():
# See https://bitbucket.org/pytest-dev/pytest/pull-request/194
# pickle.loads() raises infinite recursion if
# EncodedFile.__getattr__ is not implemented properly
Expand Down