
.. _yieldfixture:

Fixture functions using "yield" / context manager integration
---------------------------------------------------------------

.. versionadded:: 2.4

.. regendoc:wipe

pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead 
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
   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).

Let's look at a simple standalone-example using the new ``yield`` syntax::

    # content of test_yield.py
    
    import pytest

    @pytest.yield_fixture
    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
    
    1 passed in 0.00 seconds

We can also seemlessly use the new syntax with ``with`` statements.
Let's simplify the above ``passwd`` fixture::

    # content of test_yield2.py
    
    import pytest

    @pytest.yield_fixture
    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:

- usually ``yield`` is used for producing multiple values.
  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.
