fixture docs: highlight difference between yield and addfinalizer methods
Fix #2508
This commit is contained in:
		
							parent
							
								
									8c3c4307db
								
							
						
					
					
						commit
						afe847ecdc
					
				| 
						 | 
				
			
			@ -123,20 +123,13 @@ with a list of available function arguments.
 | 
			
		|||
    but is not anymore advertised as the primary means of declaring fixture
 | 
			
		||||
    functions.
 | 
			
		||||
 | 
			
		||||
"Funcargs" a prime example of dependency injection
 | 
			
		||||
Fixtures: a prime example of dependency injection
 | 
			
		||||
---------------------------------------------------
 | 
			
		||||
 | 
			
		||||
When injecting fixtures to test functions, pytest-2.0 introduced the
 | 
			
		||||
term "funcargs" or "funcarg mechanism" which continues to be present
 | 
			
		||||
also in docs today.  It now refers to the specific case of injecting
 | 
			
		||||
fixture values as arguments to test functions.  With pytest-2.3 there are
 | 
			
		||||
more possibilities to use fixtures but "funcargs" remain as the main way
 | 
			
		||||
as they allow to directly state the dependencies of a test function.
 | 
			
		||||
 | 
			
		||||
As the following examples show in more detail, funcargs allow test
 | 
			
		||||
functions to easily receive and work against specific pre-initialized
 | 
			
		||||
application objects without having to care about import/setup/cleanup
 | 
			
		||||
details.  It's a prime example of `dependency injection`_ where fixture
 | 
			
		||||
Fixtures allow test functions to easily receive and work
 | 
			
		||||
against specific pre-initialized application objects without having
 | 
			
		||||
to care about import/setup/cleanup details.
 | 
			
		||||
It's a prime example of `dependency injection`_ where fixture
 | 
			
		||||
functions take the role of the *injector* and test functions are the
 | 
			
		||||
*consumers* of fixture objects.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -296,6 +289,9 @@ The ``smtp`` connection will be closed after the test finished execution
 | 
			
		|||
because the ``smtp`` object automatically closes when
 | 
			
		||||
the ``with`` statement ends.
 | 
			
		||||
 | 
			
		||||
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
 | 
			
		||||
*teardown* code (after the ``yield``) will not be called. 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
 | 
			
		||||
| 
						 | 
				
			
			@ -303,29 +299,49 @@ the ``with`` statement ends.
 | 
			
		|||
    fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
 | 
			
		||||
    and considered deprecated.
 | 
			
		||||
 | 
			
		||||
.. note::
 | 
			
		||||
    As historical note, another way to write teardown code is
 | 
			
		||||
    by accepting a ``request`` object into your fixture function and can call its
 | 
			
		||||
    ``request.addfinalizer`` one or multiple times::
 | 
			
		||||
 | 
			
		||||
        # content of conftest.py
 | 
			
		||||
An alternative option for executing *teardown* code is to
 | 
			
		||||
make use of the ``addfinalizer`` method of the `request-context`_ object to register
 | 
			
		||||
finalization functions. 
 | 
			
		||||
 | 
			
		||||
        import smtplib
 | 
			
		||||
        import pytest
 | 
			
		||||
Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
 | 
			
		||||
 | 
			
		||||
        @pytest.fixture(scope="module")
 | 
			
		||||
        def smtp(request):
 | 
			
		||||
            smtp = smtplib.SMTP("smtp.gmail.com")
 | 
			
		||||
            def fin():
 | 
			
		||||
                print ("teardown smtp")
 | 
			
		||||
                smtp.close()
 | 
			
		||||
            request.addfinalizer(fin)
 | 
			
		||||
            return smtp  # provide the fixture value
 | 
			
		||||
.. code-block:: python
 | 
			
		||||
 | 
			
		||||
    The ``fin`` function will execute when the last test in the module has finished execution.
 | 
			
		||||
    # content of conftest.py
 | 
			
		||||
    import smtplib
 | 
			
		||||
    import pytest
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(scope="module")
 | 
			
		||||
    def smtp(request):
 | 
			
		||||
        smtp = smtplib.SMTP("smtp.gmail.com")
 | 
			
		||||
        def fin():
 | 
			
		||||
            print ("teardown smtp")
 | 
			
		||||
            smtp.close()
 | 
			
		||||
        request.addfinalizer(fin)
 | 
			
		||||
        return smtp  # provide the fixture value
 | 
			
		||||
 | 
			
		||||
Both ``yield`` and ``addfinalizer`` methods work similar by calling their code after the test
 | 
			
		||||
ends, but ``addfinalizer`` has two key differences over ``yield``:
 | 
			
		||||
 | 
			
		||||
1. It is possible to register multiple finalizer functions.
 | 
			
		||||
 | 
			
		||||
2. Finalizers will always be called regardless if the fixture *setup* code raises an exception.
 | 
			
		||||
   This is handy to properly close all resources created by a fixture even if one of them
 | 
			
		||||
   fails to be created/acquired::
 | 
			
		||||
 | 
			
		||||
        @pytest.fixture
 | 
			
		||||
        def equipments(request):
 | 
			
		||||
            r = []
 | 
			
		||||
            for port in ('C1', 'C3', 'C28'):
 | 
			
		||||
                equip = connect(port)
 | 
			
		||||
                request.addfinalizer(equip.disconnect)
 | 
			
		||||
                r.append(equip)
 | 
			
		||||
            return r
 | 
			
		||||
 | 
			
		||||
   In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still
 | 
			
		||||
   be properly closed.
 | 
			
		||||
 | 
			
		||||
    This method is still fully supported, but ``yield`` is recommended from 2.10 onward because
 | 
			
		||||
    it is considered simpler and better describes the natural code flow.
 | 
			
		||||
 | 
			
		||||
.. _`request-context`:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -782,8 +798,8 @@ Autouse fixtures (xUnit setup on steroids)
 | 
			
		|||
.. regendoc:wipe
 | 
			
		||||
 | 
			
		||||
Occasionally, you may want to have fixtures get invoked automatically
 | 
			
		||||
without a `usefixtures`_ or `funcargs`_ reference.   As a practical
 | 
			
		||||
example, suppose we have a database fixture which has a
 | 
			
		||||
without declaring a function argument explicitly or a `usefixtures`_ decorator.
 | 
			
		||||
As a practical example, suppose we have a database fixture which has a
 | 
			
		||||
begin/rollback/commit architecture and we want to automatically surround
 | 
			
		||||
each test method by a transaction and a rollback.  Here is a dummy
 | 
			
		||||
self-contained implementation of this idea::
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue