diff --git a/doc/en/example/costlysetup/conftest.py b/doc/en/example/costlysetup/conftest.py index d689c11b2..c8b9a257e 100644 --- a/doc/en/example/costlysetup/conftest.py +++ b/doc/en/example/costlysetup/conftest.py @@ -4,8 +4,8 @@ import pytest @pytest.fixture("session") def setup(request): setup = CostlySetup() - request.addfinalizer(setup.finalize) - return setup + yield setup + setup.finalize() class CostlySetup: def __init__(self): diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 6bfdc22f0..cbb46e81a 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -648,15 +648,14 @@ here is a little example implemented via a local plugin:: @pytest.fixture def something(request): - def fin(): - # request.node is an "item" because we use the default - # "function" scope - if request.node.rep_setup.failed: - print ("setting up a test failed!", request.node.nodeid) - elif request.node.rep_setup.passed: - if request.node.rep_call.failed: - print ("executing test failed", request.node.nodeid) - request.addfinalizer(fin) + yield + # request.node is an "item" because we use the default + # "function" scope + if request.node.rep_setup.failed: + print ("setting up a test failed!", request.node.nodeid) + elif request.node.rep_setup.passed: + if request.node.rep_call.failed: + print ("executing test failed", request.node.nodeid) if you then have failing tests:: diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 590649b56..dd2c1f96c 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -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 style ` or :ref:`nose based ` projects. -.. note:: - - pytest-2.4 introduced an additional :ref:`yield fixture mechanism - ` for easier context manager integration and more linear - writing of teardown code. .. _`funcargs`: .. _`funcarg mechanism`: @@ -247,9 +242,8 @@ Fixture finalization / executing teardown code ------------------------------------------------------------- pytest supports execution of fixture specific finalization code -when the fixture goes out of scope. By accepting a ``request`` object -into your fixture function you can call its ``request.addfinalizer`` one -or multiple times:: +when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all +the code after the *yield* statement serves as the teardown code.:: # content of conftest.py @@ -259,14 +253,12 @@ or multiple times:: @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 + yield smtp # provide the fixture value + print("teardown smtp") + smtp.close() -The ``fin`` function will execute when the last test using -the fixture in the module has finished execution. +The ``print`` and ``smtp.close()`` statements will execute when the last test using +the fixture in the module has finished execution, regardless of the exception status of the tests. 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 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 -fixtures*. All the code after the *yield* statement serves as the teardown -code. See the :ref:`yield fixture documentation `. + import pytest + @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`: @@ -309,12 +342,9 @@ read an optional server URL from the test module which uses our fixture:: def smtp(request): server = getattr(request.module, "smtpserver", "smtp.gmail.com") smtp = smtplib.SMTP(server) - - def fin(): - print ("finalizing %s (%s)" % (smtp, server)) - smtp.close() - request.addfinalizer(fin) - return smtp + yield smtp + print ("finalizing %s (%s)" % (smtp, server)) + smtp.close() We use the ``request.module`` attribute to optionally obtain an ``smtpserver`` attribute from the test module. If we just execute @@ -351,7 +381,7 @@ from the module namespace. .. _`fixture-parametrize`: -Parametrizing a fixture +Parametrizing fixtures ----------------------------------------------------------------- Fixture functions can be parametrized in which case they will be called @@ -374,11 +404,9 @@ through the special :py:class:`request ` object:: params=["smtp.gmail.com", "mail.python.org"]) def smtp(request): smtp = smtplib.SMTP(request.param) - def fin(): - print ("finalizing %s" % smtp) - smtp.close() - request.addfinalizer(fin) - return smtp + yield smtp + print ("finalizing %s" % smtp) + smtp.close() The main change is the declaration of ``params`` with :py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values @@ -586,19 +614,15 @@ to show the setup/teardown flow:: def modarg(request): param = request.param print (" SETUP modarg %s" % param) - def fin(): - print (" TEARDOWN modarg %s" % param) - request.addfinalizer(fin) - return param + yield param + print (" TEARDOWN modarg %s" % param) @pytest.fixture(scope="function", params=[1,2]) def otherarg(request): param = request.param print (" SETUP otherarg %s" % param) - def fin(): - print (" TEARDOWN otherarg %s" % param) - request.addfinalizer(fin) - return param + yield param + print (" TEARDOWN otherarg %s" % param) def test_0(otherarg): print (" RUN test0 with otherarg %s" % otherarg) @@ -777,7 +801,8 @@ self-contained implementation of this idea:: @pytest.fixture(autouse=True) def transact(self, request, db): db.begin(request.function.__name__) - request.addfinalizer(db.rollback) + yield + db.rollback() def test_method1(self, db): 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``:: # content of conftest.py - @pytest.fixture() + @pytest.fixture def transact(self, request, db): db.begin() - request.addfinalizer(db.rollback) + yield + db.rollback() and then e.g. have a TestClass using it by declaring the need:: diff --git a/doc/en/yieldfixture.rst b/doc/en/yieldfixture.rst index ee88a27df..02485e1ab 100644 --- a/doc/en/yieldfixture.rst +++ b/doc/en/yieldfixture.rst @@ -3,8 +3,15 @@ Fixture functions using "yield" / context manager integration --------------------------------------------------------------- +.. deprecated:: 2.10 + .. 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 pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead