Update docs in prol of using yield statements
This commit is contained in:
parent
98acda426f
commit
fe4f23c1bf
|
@ -4,8 +4,8 @@ import pytest
|
||||||
@pytest.fixture("session")
|
@pytest.fixture("session")
|
||||||
def setup(request):
|
def setup(request):
|
||||||
setup = CostlySetup()
|
setup = CostlySetup()
|
||||||
request.addfinalizer(setup.finalize)
|
yield setup
|
||||||
return setup
|
setup.finalize()
|
||||||
|
|
||||||
class CostlySetup:
|
class CostlySetup:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -648,15 +648,14 @@ here is a little example implemented via a local plugin::
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def something(request):
|
def something(request):
|
||||||
def fin():
|
yield
|
||||||
# request.node is an "item" because we use the default
|
# request.node is an "item" because we use the default
|
||||||
# "function" scope
|
# "function" scope
|
||||||
if request.node.rep_setup.failed:
|
if request.node.rep_setup.failed:
|
||||||
print ("setting up a test failed!", request.node.nodeid)
|
print ("setting up a test failed!", request.node.nodeid)
|
||||||
elif request.node.rep_setup.passed:
|
elif request.node.rep_setup.passed:
|
||||||
if request.node.rep_call.failed:
|
if request.node.rep_call.failed:
|
||||||
print ("executing test failed", request.node.nodeid)
|
print ("executing test failed", request.node.nodeid)
|
||||||
request.addfinalizer(fin)
|
|
||||||
|
|
||||||
|
|
||||||
if you then have failing tests::
|
if you then have failing tests::
|
||||||
|
|
|
@ -34,11 +34,6 @@ both styles, moving incrementally from classic to new style, as you
|
||||||
prefer. You can also start out from existing :ref:`unittest.TestCase
|
prefer. You can also start out from existing :ref:`unittest.TestCase
|
||||||
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
pytest-2.4 introduced an additional :ref:`yield fixture mechanism
|
|
||||||
<yieldfixture>` for easier context manager integration and more linear
|
|
||||||
writing of teardown code.
|
|
||||||
|
|
||||||
.. _`funcargs`:
|
.. _`funcargs`:
|
||||||
.. _`funcarg mechanism`:
|
.. _`funcarg mechanism`:
|
||||||
|
@ -247,9 +242,8 @@ Fixture finalization / executing teardown code
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
pytest supports execution of fixture specific finalization code
|
pytest supports execution of fixture specific finalization code
|
||||||
when the fixture goes out of scope. By accepting a ``request`` object
|
when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all
|
||||||
into your fixture function you can call its ``request.addfinalizer`` one
|
the code after the *yield* statement serves as the teardown code.::
|
||||||
or multiple times::
|
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
|
@ -259,14 +253,12 @@ or multiple times::
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp(request):
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com")
|
smtp = smtplib.SMTP("smtp.gmail.com")
|
||||||
def fin():
|
yield smtp # provide the fixture value
|
||||||
print ("teardown smtp")
|
print("teardown smtp")
|
||||||
smtp.close()
|
smtp.close()
|
||||||
request.addfinalizer(fin)
|
|
||||||
return smtp # provide the fixture value
|
|
||||||
|
|
||||||
The ``fin`` function will execute when the last test using
|
The ``print`` and ``smtp.close()`` statements will execute when the last test using
|
||||||
the fixture in the module has finished execution.
|
the fixture in the module has finished execution, regardless of the exception status of the tests.
|
||||||
|
|
||||||
Let's execute it::
|
Let's execute it::
|
||||||
|
|
||||||
|
@ -282,14 +274,55 @@ occur around each single test. In either case the test
|
||||||
module itself does not need to change or know about these details
|
module itself does not need to change or know about these details
|
||||||
of fixture setup.
|
of fixture setup.
|
||||||
|
|
||||||
|
Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements::
|
||||||
|
|
||||||
Finalization/teardown with yield fixtures
|
# content of test_yield2.py
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Another alternative to the *request.addfinalizer()* method is to use *yield
|
import pytest
|
||||||
fixtures*. All the code after the *yield* statement serves as the teardown
|
|
||||||
code. See the :ref:`yield fixture documentation <yieldfixture>`.
|
|
||||||
|
|
||||||
|
@pytest.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::
|
||||||
|
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
|
||||||
|
had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
The ``fin`` function will execute when the last test using
|
||||||
|
the fixture in the module has finished execution.
|
||||||
|
|
||||||
|
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`:
|
||||||
|
|
||||||
|
@ -309,12 +342,9 @@ read an optional server URL from the test module which uses our fixture::
|
||||||
def smtp(request):
|
def smtp(request):
|
||||||
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
||||||
smtp = smtplib.SMTP(server)
|
smtp = smtplib.SMTP(server)
|
||||||
|
yield smtp
|
||||||
def fin():
|
print ("finalizing %s (%s)" % (smtp, server))
|
||||||
print ("finalizing %s (%s)" % (smtp, server))
|
smtp.close()
|
||||||
smtp.close()
|
|
||||||
request.addfinalizer(fin)
|
|
||||||
return smtp
|
|
||||||
|
|
||||||
We use the ``request.module`` attribute to optionally obtain an
|
We use the ``request.module`` attribute to optionally obtain an
|
||||||
``smtpserver`` attribute from the test module. If we just execute
|
``smtpserver`` attribute from the test module. If we just execute
|
||||||
|
@ -351,7 +381,7 @@ from the module namespace.
|
||||||
|
|
||||||
.. _`fixture-parametrize`:
|
.. _`fixture-parametrize`:
|
||||||
|
|
||||||
Parametrizing a fixture
|
Parametrizing fixtures
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
|
|
||||||
Fixture functions can be parametrized in which case they will be called
|
Fixture functions can be parametrized in which case they will be called
|
||||||
|
@ -374,11 +404,9 @@ through the special :py:class:`request <FixtureRequest>` object::
|
||||||
params=["smtp.gmail.com", "mail.python.org"])
|
params=["smtp.gmail.com", "mail.python.org"])
|
||||||
def smtp(request):
|
def smtp(request):
|
||||||
smtp = smtplib.SMTP(request.param)
|
smtp = smtplib.SMTP(request.param)
|
||||||
def fin():
|
yield smtp
|
||||||
print ("finalizing %s" % smtp)
|
print ("finalizing %s" % smtp)
|
||||||
smtp.close()
|
smtp.close()
|
||||||
request.addfinalizer(fin)
|
|
||||||
return smtp
|
|
||||||
|
|
||||||
The main change is the declaration of ``params`` with
|
The main change is the declaration of ``params`` with
|
||||||
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
|
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
|
||||||
|
@ -586,19 +614,15 @@ to show the setup/teardown flow::
|
||||||
def modarg(request):
|
def modarg(request):
|
||||||
param = request.param
|
param = request.param
|
||||||
print (" SETUP modarg %s" % param)
|
print (" SETUP modarg %s" % param)
|
||||||
def fin():
|
yield param
|
||||||
print (" TEARDOWN modarg %s" % param)
|
print (" TEARDOWN modarg %s" % param)
|
||||||
request.addfinalizer(fin)
|
|
||||||
return param
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function", params=[1,2])
|
@pytest.fixture(scope="function", params=[1,2])
|
||||||
def otherarg(request):
|
def otherarg(request):
|
||||||
param = request.param
|
param = request.param
|
||||||
print (" SETUP otherarg %s" % param)
|
print (" SETUP otherarg %s" % param)
|
||||||
def fin():
|
yield param
|
||||||
print (" TEARDOWN otherarg %s" % param)
|
print (" TEARDOWN otherarg %s" % param)
|
||||||
request.addfinalizer(fin)
|
|
||||||
return param
|
|
||||||
|
|
||||||
def test_0(otherarg):
|
def test_0(otherarg):
|
||||||
print (" RUN test0 with otherarg %s" % otherarg)
|
print (" RUN test0 with otherarg %s" % otherarg)
|
||||||
|
@ -777,7 +801,8 @@ self-contained implementation of this idea::
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def transact(self, request, db):
|
def transact(self, request, db):
|
||||||
db.begin(request.function.__name__)
|
db.begin(request.function.__name__)
|
||||||
request.addfinalizer(db.rollback)
|
yield
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
def test_method1(self, db):
|
def test_method1(self, db):
|
||||||
assert db.intransaction == ["test_method1"]
|
assert db.intransaction == ["test_method1"]
|
||||||
|
@ -817,10 +842,11 @@ active. The canonical way to do that is to put the transact definition
|
||||||
into a conftest.py file **without** using ``autouse``::
|
into a conftest.py file **without** using ``autouse``::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def transact(self, request, db):
|
def transact(self, request, db):
|
||||||
db.begin()
|
db.begin()
|
||||||
request.addfinalizer(db.rollback)
|
yield
|
||||||
|
db.rollback()
|
||||||
|
|
||||||
and then e.g. have a TestClass using it by declaring the need::
|
and then e.g. have a TestClass using it by declaring the need::
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,15 @@
|
||||||
Fixture functions using "yield" / context manager integration
|
Fixture functions using "yield" / context manager integration
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
|
||||||
|
.. deprecated:: 2.10
|
||||||
|
|
||||||
.. versionadded:: 2.4
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
Since pytest-2.10, fixtures using the normal ``fixture`` decorator can use a ``yield``
|
||||||
|
statement to provide fixture values and execute teardown code, exactly like ``yield_fixture``
|
||||||
|
described in this session.
|
||||||
|
|
||||||
.. regendoc:wipe
|
.. regendoc:wipe
|
||||||
|
|
||||||
pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead
|
pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead
|
||||||
|
|
Loading…
Reference in New Issue