Skip to content

Commit afe847e

Browse files
committed
fixture docs: highlight difference between yield and addfinalizer methods
Fix #2508
1 parent 8c3c430 commit afe847e

File tree

1 file changed

+48
-32
lines changed

1 file changed

+48
-32
lines changed

doc/en/fixture.rst

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,13 @@ with a list of available function arguments.
123123
but is not anymore advertised as the primary means of declaring fixture
124124
functions.
125125

126-
"Funcargs" a prime example of dependency injection
126+
Fixtures: a prime example of dependency injection
127127
---------------------------------------------------
128128

129-
When injecting fixtures to test functions, pytest-2.0 introduced the
130-
term "funcargs" or "funcarg mechanism" which continues to be present
131-
also in docs today. It now refers to the specific case of injecting
132-
fixture values as arguments to test functions. With pytest-2.3 there are
133-
more possibilities to use fixtures but "funcargs" remain as the main way
134-
as they allow to directly state the dependencies of a test function.
135-
136-
As the following examples show in more detail, funcargs allow test
137-
functions to easily receive and work against specific pre-initialized
138-
application objects without having to care about import/setup/cleanup
139-
details. It's a prime example of `dependency injection`_ where fixture
129+
Fixtures allow test functions to easily receive and work
130+
against specific pre-initialized application objects without having
131+
to care about import/setup/cleanup details.
132+
It's a prime example of `dependency injection`_ where fixture
140133
functions take the role of the *injector* and test functions are the
141134
*consumers* of fixture objects.
142135

@@ -296,36 +289,59 @@ The ``smtp`` connection will be closed after the test finished execution
296289
because the ``smtp`` object automatically closes when
297290
the ``with`` statement ends.
298291

292+
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
293+
*teardown* code (after the ``yield``) will not be called.
294+
299295

300296
.. note::
301297
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
302298
had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
303299
fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
304300
and considered deprecated.
305301

306-
.. note::
307-
As historical note, another way to write teardown code is
308-
by accepting a ``request`` object into your fixture function and can call its
309-
``request.addfinalizer`` one or multiple times::
310302

311-
# content of conftest.py
303+
An alternative option for executing *teardown* code is to
304+
make use of the ``addfinalizer`` method of the `request-context`_ object to register
305+
finalization functions.
312306

313-
import smtplib
314-
import pytest
307+
Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
308+
309+
.. code-block:: python
310+
311+
# content of conftest.py
312+
import smtplib
313+
import pytest
314+
315+
@pytest.fixture(scope="module")
316+
def smtp(request):
317+
smtp = smtplib.SMTP("smtp.gmail.com")
318+
def fin():
319+
print ("teardown smtp")
320+
smtp.close()
321+
request.addfinalizer(fin)
322+
return smtp # provide the fixture value
323+
324+
Both ``yield`` and ``addfinalizer`` methods work similar by calling their code after the test
325+
ends, but ``addfinalizer`` has two key differences over ``yield``:
326+
327+
1. It is possible to register multiple finalizer functions.
328+
329+
2. Finalizers will always be called regardless if the fixture *setup* code raises an exception.
330+
This is handy to properly close all resources created by a fixture even if one of them
331+
fails to be created/acquired::
315332

316-
@pytest.fixture(scope="module")
317-
def smtp(request):
318-
smtp = smtplib.SMTP("smtp.gmail.com")
319-
def fin():
320-
print ("teardown smtp")
321-
smtp.close()
322-
request.addfinalizer(fin)
323-
return smtp # provide the fixture value
333+
@pytest.fixture
334+
def equipments(request):
335+
r = []
336+
for port in ('C1', 'C3', 'C28'):
337+
equip = connect(port)
338+
request.addfinalizer(equip.disconnect)
339+
r.append(equip)
340+
return r
324341

325-
The ``fin`` function will execute when the last test in the module has finished execution.
342+
In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still
343+
be properly closed.
326344

327-
This method is still fully supported, but ``yield`` is recommended from 2.10 onward because
328-
it is considered simpler and better describes the natural code flow.
329345

330346
.. _`request-context`:
331347

@@ -782,8 +798,8 @@ Autouse fixtures (xUnit setup on steroids)
782798
.. regendoc:wipe
783799
784800
Occasionally, you may want to have fixtures get invoked automatically
785-
without a `usefixtures`_ or `funcargs`_ reference. As a practical
786-
example, suppose we have a database fixture which has a
801+
without declaring a function argument explicitly or a `usefixtures`_ decorator.
802+
As a practical example, suppose we have a database fixture which has a
787803
begin/rollback/commit architecture and we want to automatically surround
788804
each test method by a transaction and a rollback. Here is a dummy
789805
self-contained implementation of this idea::

0 commit comments

Comments
 (0)