2013-09-30 19:42:39 +08:00
|
|
|
.. _yieldfixture:
|
2013-09-27 16:21:23 +08:00
|
|
|
|
|
|
|
Fixture functions using "yield" / context manager integration
|
|
|
|
---------------------------------------------------------------
|
|
|
|
|
|
|
|
.. versionadded:: 2.4
|
|
|
|
|
|
|
|
.. regendoc:wipe
|
|
|
|
|
2013-10-01 20:30:53 +08:00
|
|
|
pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead
|
2013-09-27 16:21:23 +08:00
|
|
|
of a ``return`` statement to provide a fixture value while otherwise
|
|
|
|
fully supporting all other fixture features.
|
|
|
|
|
|
|
|
.. note::
|
|
|
|
|
|
|
|
"yielding" fixture values is an experimental feature and its exact
|
|
|
|
declaration may change later but earliest in a 2.5 release. You can thus
|
2013-09-30 19:42:39 +08:00
|
|
|
safely use this feature in the 2.4 series but may need to adapt later.
|
|
|
|
Test functions themselves will not need to change (as a general
|
|
|
|
feature, they are ignorant of how fixtures are setup).
|
2013-09-27 16:21:23 +08:00
|
|
|
|
|
|
|
Let's look at a simple standalone-example using the new ``yield`` syntax::
|
|
|
|
|
|
|
|
# content of test_yield.py
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2013-09-30 19:42:39 +08:00
|
|
|
@pytest.yield_fixture
|
2013-09-27 16:21:23 +08:00
|
|
|
def passwd():
|
|
|
|
print ("\nsetup before yield")
|
|
|
|
f = open("/etc/passwd")
|
|
|
|
yield f.readlines()
|
|
|
|
print ("teardown after yield")
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def test_has_lines(passwd):
|
|
|
|
print ("test called")
|
|
|
|
assert passwd
|
|
|
|
|
|
|
|
In contrast to :ref:`finalization through registering callbacks
|
|
|
|
<finalization>`, our fixture function used a ``yield``
|
|
|
|
statement to provide the lines of the ``/etc/passwd`` file.
|
|
|
|
The code after the ``yield`` statement serves as the teardown code,
|
|
|
|
avoiding the indirection of registering a teardown callback function.
|
|
|
|
|
|
|
|
Let's run it with output capturing disabled::
|
|
|
|
|
|
|
|
$ py.test -q -s test_yield.py
|
|
|
|
|
|
|
|
setup before yield
|
|
|
|
test called
|
|
|
|
.teardown after yield
|
|
|
|
|
2015-03-26 16:34:10 +08:00
|
|
|
1 passed in 0.00 seconds
|
2013-09-27 16:21:23 +08:00
|
|
|
|
2014-07-10 06:00:24 +08:00
|
|
|
We can also seamlessly use the new syntax with ``with`` statements.
|
2013-09-27 16:21:23 +08:00
|
|
|
Let's simplify the above ``passwd`` fixture::
|
|
|
|
|
|
|
|
# content of test_yield2.py
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
2013-09-30 19:42:39 +08:00
|
|
|
@pytest.yield_fixture
|
2013-09-27 16:21:23 +08:00
|
|
|
def passwd():
|
|
|
|
with open("/etc/passwd") as f:
|
|
|
|
yield f.readlines()
|
|
|
|
|
|
|
|
def test_has_lines(passwd):
|
|
|
|
assert len(passwd) >= 1
|
|
|
|
|
|
|
|
The file ``f`` will be closed after the test finished execution
|
|
|
|
because the Python ``file`` object supports finalization when
|
|
|
|
the ``with`` statement ends.
|
|
|
|
|
|
|
|
Note that the new syntax is fully integrated with using ``scope``,
|
|
|
|
``params`` and other fixture features. Changing existing
|
|
|
|
fixture functions to use ``yield`` is thus straight forward.
|
|
|
|
|
|
|
|
Discussion and future considerations / feedback
|
|
|
|
++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
The yield-syntax has been discussed by pytest users extensively.
|
|
|
|
In general, the advantages of the using a ``yield`` fixture syntax are:
|
|
|
|
|
|
|
|
- easy provision of fixtures in conjunction with context managers.
|
|
|
|
|
|
|
|
- no need to register a callback, providing for more synchronous
|
|
|
|
control flow in the fixture function. Also there is no need to accept
|
|
|
|
the ``request`` object into the fixture function just for providing
|
|
|
|
finalization code.
|
|
|
|
|
|
|
|
However, there are also limitations or foreseeable irritations:
|
|
|
|
|
2013-10-01 20:30:53 +08:00
|
|
|
- usually ``yield`` is used for producing multiple values.
|
2013-09-27 16:21:23 +08:00
|
|
|
But fixture functions can only yield exactly one value.
|
|
|
|
Yielding a second fixture value will get you an error.
|
|
|
|
It's possible we can evolve pytest to allow for producing
|
|
|
|
multiple values as an alternative to current parametrization.
|
|
|
|
For now, you can just use the normal
|
|
|
|
:ref:`fixture parametrization <fixture-parametrize>`
|
|
|
|
mechanisms together with ``yield``-style fixtures.
|
|
|
|
|
|
|
|
- the ``yield`` syntax is similar to what
|
|
|
|
:py:func:`contextlib.contextmanager` decorated functions
|
|
|
|
provide. With pytest fixture functions, the "after yield" part will
|
|
|
|
always be invoked, independently from the exception status
|
|
|
|
of the test function which uses the fixture. The pytest
|
|
|
|
behaviour makes sense if you consider that many different
|
|
|
|
test functions might use a module or session scoped fixture.
|
|
|
|
Some test functions might raise exceptions and others not,
|
|
|
|
so how could pytest re-raise a single exception at the
|
|
|
|
``yield`` point in the fixture function?
|
|
|
|
|
|
|
|
- lastly ``yield`` introduces more than one way to write
|
|
|
|
fixture functions, so what's the obvious way to a newcomer?
|
|
|
|
Newcomers reading the docs will see feature examples using the
|
|
|
|
``return`` style so should use that, if in doubt.
|
|
|
|
Others can start experimenting with writing yield-style fixtures
|
|
|
|
and possibly help evolving them further.
|
|
|
|
|
|
|
|
If you want to feedback or participate in the ongoing
|
|
|
|
discussion, please join our :ref:`contact channels`.
|
|
|
|
you are most welcome.
|