strike keyword argument in favour of new pytest.yield_fixture decorator
This commit is contained in:
parent
431ce79d94
commit
086d4e4ced
|
@ -16,9 +16,12 @@ known incompatibilities:
|
||||||
|
|
||||||
new features:
|
new features:
|
||||||
|
|
||||||
- experimentally allow fixture functions to "yield" instead of "return"
|
- experimentally introduce a new pytest.yield_fixture decorator which
|
||||||
a fixture value, allowing direct integration with with-context managers
|
has exactly the same parameters as pytest.fixture but expects
|
||||||
in fixture functions and avoiding registration of finalization callbacks.
|
a ``yield`` statement instead of a ``return statement`` from fixture functions.
|
||||||
|
This allows direct integration with with-context managers
|
||||||
|
in fixture functions and generally avoids registering of finalization callbacks
|
||||||
|
in favour of treating the "after-yield" as teardown code.
|
||||||
Thanks Andreas Pelme, Vladimir Keleshev, Floris Bruynooghe, Ronny Pfannschmidt
|
Thanks Andreas Pelme, Vladimir Keleshev, Floris Bruynooghe, Ronny Pfannschmidt
|
||||||
and many others for discussions.
|
and many others for discussions.
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ class FixtureFunctionMarker:
|
||||||
return function
|
return function
|
||||||
|
|
||||||
|
|
||||||
def fixture(scope="function", params=None, autouse=False, yieldctx=False):
|
def fixture(scope="function", params=None, autouse=False):
|
||||||
""" (return a) decorator to mark a fixture factory function.
|
""" (return a) decorator to mark a fixture factory function.
|
||||||
|
|
||||||
This decorator can be used (with or or without parameters) to define
|
This decorator can be used (with or or without parameters) to define
|
||||||
|
@ -62,16 +62,29 @@ def fixture(scope="function", params=None, autouse=False, yieldctx=False):
|
||||||
can see it. If False (the default) then an explicit
|
can see it. If False (the default) then an explicit
|
||||||
reference is needed to activate the fixture.
|
reference is needed to activate the fixture.
|
||||||
|
|
||||||
:arg yieldctx: if True, the fixture function yields a fixture value.
|
|
||||||
Code after such a ``yield`` statement is treated as
|
|
||||||
teardown code.
|
|
||||||
"""
|
"""
|
||||||
if callable(scope) and params is None and autouse == False:
|
if callable(scope) and params is None and autouse == False:
|
||||||
# direct decoration
|
# direct decoration
|
||||||
return FixtureFunctionMarker(
|
return FixtureFunctionMarker(
|
||||||
"function", params, autouse, yieldctx)(scope)
|
"function", params, autouse)(scope)
|
||||||
else:
|
else:
|
||||||
return FixtureFunctionMarker(scope, params, autouse, yieldctx)
|
return FixtureFunctionMarker(scope, params, autouse)
|
||||||
|
|
||||||
|
def yield_fixture(scope="function", params=None, autouse=False):
|
||||||
|
""" (return a) decorator to mark a yield-fixture factory function
|
||||||
|
(EXPERIMENTAL).
|
||||||
|
|
||||||
|
This takes the same arguments as :py:func:`pytest.fixture` but
|
||||||
|
expects a fixture function to use a ``yield`` instead of a ``return``
|
||||||
|
statement to provide a fixture. See
|
||||||
|
http://pytest.org/en/latest/yieldfixture.html for more info.
|
||||||
|
"""
|
||||||
|
if callable(scope) and params is None and autouse == False:
|
||||||
|
# direct decoration
|
||||||
|
return FixtureFunctionMarker(
|
||||||
|
"function", params, autouse, yieldctx=True)(scope)
|
||||||
|
else:
|
||||||
|
return FixtureFunctionMarker(scope, params, autouse, yieldctx=True)
|
||||||
|
|
||||||
defaultfuncargprefixmarker = fixture()
|
defaultfuncargprefixmarker = fixture()
|
||||||
|
|
||||||
|
@ -136,6 +149,7 @@ def pytest_namespace():
|
||||||
raises.Exception = pytest.fail.Exception
|
raises.Exception = pytest.fail.Exception
|
||||||
return {
|
return {
|
||||||
'fixture': fixture,
|
'fixture': fixture,
|
||||||
|
'yield_fixture': yield_fixture,
|
||||||
'raises' : raises,
|
'raises' : raises,
|
||||||
'collect': {
|
'collect': {
|
||||||
'Module': Module, 'Class': Class, 'Instance': Instance,
|
'Module': Module, 'Class': Class, 'Instance': Instance,
|
||||||
|
@ -1675,7 +1689,7 @@ def call_fixture_func(fixturefunc, request, kwargs, yieldctx):
|
||||||
if yieldctx:
|
if yieldctx:
|
||||||
if not is_generator(fixturefunc):
|
if not is_generator(fixturefunc):
|
||||||
fail_fixturefunc(fixturefunc,
|
fail_fixturefunc(fixturefunc,
|
||||||
msg="yieldctx=True requires yield statement")
|
msg="yield_fixture requires yield statement in function")
|
||||||
iter = fixturefunc(**kwargs)
|
iter = fixturefunc(**kwargs)
|
||||||
next = getattr(iter, "__next__", None)
|
next = getattr(iter, "__next__", None)
|
||||||
if next is None:
|
if next is None:
|
||||||
|
@ -1688,7 +1702,7 @@ def call_fixture_func(fixturefunc, request, kwargs, yieldctx):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
fail_fixturefunc(fixturefunc,
|
fail_fixturefunc(fixturefunc,
|
||||||
"fixture function has more than one 'yield'")
|
"yield_fixture function has more than one 'yield'")
|
||||||
request.addfinalizer(teardown)
|
request.addfinalizer(teardown)
|
||||||
else:
|
else:
|
||||||
res = fixturefunc(**kwargs)
|
res = fixturefunc(**kwargs)
|
||||||
|
|
|
@ -276,8 +276,8 @@ module itself does not need to change or know about these details
|
||||||
of fixture setup.
|
of fixture setup.
|
||||||
|
|
||||||
Note that pytest-2.4 introduced an experimental alternative
|
Note that pytest-2.4 introduced an experimental alternative
|
||||||
:ref:`yield fixture mechanism <yieldctx>` for easier context manager integration
|
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
|
||||||
and more linear writing of teardown code.
|
integration and more linear writing of teardown code.
|
||||||
|
|
||||||
.. _`request-context`:
|
.. _`request-context`:
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
.. _yieldctx:
|
.. _yieldfixture:
|
||||||
|
|
||||||
Fixture functions using "yield" / context manager integration
|
Fixture functions using "yield" / context manager integration
|
||||||
---------------------------------------------------------------
|
---------------------------------------------------------------
|
||||||
|
@ -16,10 +16,9 @@ fully supporting all other fixture features.
|
||||||
|
|
||||||
"yielding" fixture values is an experimental feature and its exact
|
"yielding" fixture values is an experimental feature and its exact
|
||||||
declaration may change later but earliest in a 2.5 release. You can thus
|
declaration may change later but earliest in a 2.5 release. You can thus
|
||||||
safely use this feature in the 2.4 series but may need to adapt your
|
safely use this feature in the 2.4 series but may need to adapt later.
|
||||||
fixtures later. Test functions themselves will not need to change
|
Test functions themselves will not need to change (as a general
|
||||||
(they can be completely ignorant of the return/yield modes of
|
feature, they are ignorant of how fixtures are setup).
|
||||||
fixture functions).
|
|
||||||
|
|
||||||
Let's look at a simple standalone-example using the new ``yield`` syntax::
|
Let's look at a simple standalone-example using the new ``yield`` syntax::
|
||||||
|
|
||||||
|
@ -27,7 +26,7 @@ Let's look at a simple standalone-example using the new ``yield`` syntax::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(yieldctx=True)
|
@pytest.yield_fixture
|
||||||
def passwd():
|
def passwd():
|
||||||
print ("\nsetup before yield")
|
print ("\nsetup before yield")
|
||||||
f = open("/etc/passwd")
|
f = open("/etc/passwd")
|
||||||
|
@ -62,7 +61,7 @@ Let's simplify the above ``passwd`` fixture::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture(yieldctx=True)
|
@pytest.yield_fixture
|
||||||
def passwd():
|
def passwd():
|
||||||
with open("/etc/passwd") as f:
|
with open("/etc/passwd") as f:
|
||||||
yield f.readlines()
|
yield f.readlines()
|
||||||
|
|
|
@ -1980,7 +1980,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_simple(self, testdir):
|
def test_simple(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(yieldctx=True)
|
@pytest.yield_fixture
|
||||||
def arg1():
|
def arg1():
|
||||||
print ("setup")
|
print ("setup")
|
||||||
yield 1
|
yield 1
|
||||||
|
@ -2004,7 +2004,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_scoped(self, testdir):
|
def test_scoped(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope="module", yieldctx=True)
|
@pytest.yield_fixture(scope="module")
|
||||||
def arg1():
|
def arg1():
|
||||||
print ("setup")
|
print ("setup")
|
||||||
yield 1
|
yield 1
|
||||||
|
@ -2025,7 +2025,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_setup_exception(self, testdir):
|
def test_setup_exception(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope="module", yieldctx=True)
|
@pytest.yield_fixture(scope="module")
|
||||||
def arg1():
|
def arg1():
|
||||||
pytest.fail("setup")
|
pytest.fail("setup")
|
||||||
yield 1
|
yield 1
|
||||||
|
@ -2041,7 +2041,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_teardown_exception(self, testdir):
|
def test_teardown_exception(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope="module", yieldctx=True)
|
@pytest.yield_fixture(scope="module")
|
||||||
def arg1():
|
def arg1():
|
||||||
yield 1
|
yield 1
|
||||||
pytest.fail("teardown")
|
pytest.fail("teardown")
|
||||||
|
@ -2057,7 +2057,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_yields_more_than_one(self, testdir):
|
def test_yields_more_than_one(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope="module", yieldctx=True)
|
@pytest.yield_fixture(scope="module")
|
||||||
def arg1():
|
def arg1():
|
||||||
yield 1
|
yield 1
|
||||||
yield 2
|
yield 2
|
||||||
|
@ -2074,7 +2074,7 @@ class TestContextManagerFixtureFuncs:
|
||||||
def test_no_yield(self, testdir):
|
def test_no_yield(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
@pytest.fixture(scope="module", yieldctx=True)
|
@pytest.yield_fixture(scope="module")
|
||||||
def arg1():
|
def arg1():
|
||||||
return 1
|
return 1
|
||||||
def test_1(arg1):
|
def test_1(arg1):
|
||||||
|
@ -2082,9 +2082,8 @@ class TestContextManagerFixtureFuncs:
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest("-s")
|
result = testdir.runpytest("-s")
|
||||||
result.stdout.fnmatch_lines("""
|
result.stdout.fnmatch_lines("""
|
||||||
*yieldctx*requires*yield*
|
*yield_fixture*requires*yield*
|
||||||
*yieldctx=True*
|
*yield_fixture*
|
||||||
*def arg1*
|
*def arg1*
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue