Merge pull request #2511 from nicoddemus/addfinalizer-docs
fixture docs: highlight difference between yield and addfinalizer methods
This commit is contained in:
commit
4a62102b57
|
@ -123,20 +123,13 @@ with a list of available function arguments.
|
||||||
but is not anymore advertised as the primary means of declaring fixture
|
but is not anymore advertised as the primary means of declaring fixture
|
||||||
functions.
|
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
|
Fixtures allow test functions to easily receive and work
|
||||||
term "funcargs" or "funcarg mechanism" which continues to be present
|
against specific pre-initialized application objects without having
|
||||||
also in docs today. It now refers to the specific case of injecting
|
to care about import/setup/cleanup details.
|
||||||
fixture values as arguments to test functions. With pytest-2.3 there are
|
It's a prime example of `dependency injection`_ where fixture
|
||||||
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
|
|
||||||
functions take the role of the *injector* and test functions are the
|
functions take the role of the *injector* and test functions are the
|
||||||
*consumers* of fixture objects.
|
*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
|
because the ``smtp`` object automatically closes when
|
||||||
the ``with`` statement ends.
|
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::
|
.. note::
|
||||||
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
|
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
|
||||||
|
@ -303,29 +299,51 @@ the ``with`` statement ends.
|
||||||
fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
|
fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
|
||||||
and considered deprecated.
|
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
|
Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
.. code-block:: python
|
||||||
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
|
|
||||||
|
|
||||||
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 similarly 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. Of course, if an exception happens before the finalize function is
|
||||||
|
registered then it will not be executed.
|
||||||
|
|
||||||
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`:
|
.. _`request-context`:
|
||||||
|
|
||||||
|
@ -782,8 +800,8 @@ Autouse fixtures (xUnit setup on steroids)
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
Occasionally, you may want to have fixtures get invoked automatically
|
Occasionally, you may want to have fixtures get invoked automatically
|
||||||
without a `usefixtures`_ or `funcargs`_ reference. As a practical
|
without declaring a function argument explicitly or a `usefixtures`_ decorator.
|
||||||
example, suppose we have a database fixture which has a
|
As a practical example, suppose we have a database fixture which has a
|
||||||
begin/rollback/commit architecture and we want to automatically surround
|
begin/rollback/commit architecture and we want to automatically surround
|
||||||
each test method by a transaction and a rollback. Here is a dummy
|
each test method by a transaction and a rollback. Here is a dummy
|
||||||
self-contained implementation of this idea::
|
self-contained implementation of this idea::
|
||||||
|
|
Loading…
Reference in New Issue