Merge pull request #1586 from nicoddemus/issue-1461-merge-yield-fixture

Make normal fixtures work with "yield"
This commit is contained in:
Ronny Pfannschmidt 2016-06-14 10:31:09 +02:00 committed by GitHub
commit feeee2803e
7 changed files with 138 additions and 230 deletions

View File

@ -36,6 +36,12 @@
**Changes** **Changes**
* Fixtures marked with ``@pytest.fixture`` can now use ``yield`` statements exactly like
those marked with the ``@pytest.yield_fixture`` decorator. This change renders
``@pytest.yield_fixture`` deprecated and makes ``@pytest.fixture`` with ``yield`` statements
the preferred way to write teardown code (`#1461`_).
Thanks `@csaftoiu`_ for bringing this to attention and `@nicoddemus`_ for the PR.
* Fix (`#1351`_): * Fix (`#1351`_):
explicitly passed parametrize ids do not get escaped to ascii. explicitly passed parametrize ids do not get escaped to ascii.
Thanks `@ceridwen`_ for the PR. Thanks `@ceridwen`_ for the PR.
@ -58,6 +64,7 @@
* *
.. _@milliams: https://github.com/milliams .. _@milliams: https://github.com/milliams
.. _@csaftoiu: https://github.com/csaftoiu
.. _@novas0x2a: https://github.com/novas0x2a .. _@novas0x2a: https://github.com/novas0x2a
.. _@kalekundert: https://github.com/kalekundert .. _@kalekundert: https://github.com/kalekundert
.. _@tareqalayan: https://github.com/tareqalayan .. _@tareqalayan: https://github.com/tareqalayan
@ -72,6 +79,7 @@
.. _#1441: https://github.com/pytest-dev/pytest/pull/1441 .. _#1441: https://github.com/pytest-dev/pytest/pull/1441
.. _#1454: https://github.com/pytest-dev/pytest/pull/1454 .. _#1454: https://github.com/pytest-dev/pytest/pull/1454
.. _#1351: https://github.com/pytest-dev/pytest/issues/1351 .. _#1351: https://github.com/pytest-dev/pytest/issues/1351
.. _#1461: https://github.com/pytest-dev/pytest/pull/1461
.. _#1468: https://github.com/pytest-dev/pytest/pull/1468 .. _#1468: https://github.com/pytest-dev/pytest/pull/1468
.. _#1474: https://github.com/pytest-dev/pytest/pull/1474 .. _#1474: https://github.com/pytest-dev/pytest/pull/1474
.. _#1502: https://github.com/pytest-dev/pytest/pull/1502 .. _#1502: https://github.com/pytest-dev/pytest/pull/1502

View File

@ -116,12 +116,10 @@ def safe_getattr(object, name, default):
class FixtureFunctionMarker: class FixtureFunctionMarker:
def __init__(self, scope, params, def __init__(self, scope, params, autouse=False, ids=None, name=None):
autouse=False, yieldctx=False, ids=None, name=None):
self.scope = scope self.scope = scope
self.params = params self.params = params
self.autouse = autouse self.autouse = autouse
self.yieldctx = yieldctx
self.ids = ids self.ids = ids
self.name = name self.name = name
@ -166,6 +164,10 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
to resolve this is to name the decorated function to resolve this is to name the decorated function
``fixture_<fixturename>`` and then use ``fixture_<fixturename>`` and then use
``@pytest.fixture(name='<fixturename>')``. ``@pytest.fixture(name='<fixturename>')``.
Fixtures can optionally provide their values to test functions using a ``yield`` statement,
instead of ``return``. In this case, the code block after the ``yield`` statement is executed
as teardown code regardless of the test outcome. A fixture function must yield exactly once.
""" """
if callable(scope) and params is None and autouse == False: if callable(scope) and params is None and autouse == False:
# direct decoration # direct decoration
@ -175,22 +177,19 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
params = list(params) params = list(params)
return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
def yield_fixture(scope="function", params=None, autouse=False, ids=None):
""" (return a) decorator to mark a yield-fixture factory function
(EXPERIMENTAL).
This takes the same arguments as :py:func:`pytest.fixture` but def yield_fixture(scope="function", params=None, autouse=False, ids=None, name=None):
expects a fixture function to use a ``yield`` instead of a ``return`` """ (return a) decorator to mark a yield-fixture factory function.
statement to provide a fixture. See
http://pytest.org/en/latest/yieldfixture.html for more info. .. deprecated:: 1.10
Use :py:func:`pytest.fixture` directly instead.
""" """
if callable(scope) and params is None and autouse == False: if callable(scope) and params is None and not autouse:
# direct decoration # direct decoration
return FixtureFunctionMarker( return FixtureFunctionMarker(
"function", params, autouse, yieldctx=True)(scope) "function", params, autouse, ids=ids, name=name)(scope)
else: else:
return FixtureFunctionMarker(scope, params, autouse, return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
yieldctx=True, ids=ids)
defaultfuncargprefixmarker = fixture() defaultfuncargprefixmarker = fixture()
@ -2287,7 +2286,6 @@ class FixtureManager:
assert not name.startswith(self._argprefix) assert not name.startswith(self._argprefix)
fixturedef = FixtureDef(self, nodeid, name, obj, fixturedef = FixtureDef(self, nodeid, name, obj,
marker.scope, marker.params, marker.scope, marker.params,
yieldctx=marker.yieldctx,
unittest=unittest, ids=marker.ids) unittest=unittest, ids=marker.ids)
faclist = self._arg2fixturedefs.setdefault(name, []) faclist = self._arg2fixturedefs.setdefault(name, [])
if fixturedef.has_location: if fixturedef.has_location:
@ -2325,38 +2323,30 @@ def fail_fixturefunc(fixturefunc, msg):
pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location,
pytrace=False) pytrace=False)
def call_fixture_func(fixturefunc, request, kwargs, yieldctx): def call_fixture_func(fixturefunc, request, kwargs):
yieldctx = is_generator(fixturefunc)
if yieldctx: if yieldctx:
if not is_generator(fixturefunc): it = fixturefunc(**kwargs)
fail_fixturefunc(fixturefunc, res = next(it)
msg="yield_fixture requires yield statement in function")
iter = fixturefunc(**kwargs)
next = getattr(iter, "__next__", None)
if next is None:
next = getattr(iter, "next")
res = next()
def teardown(): def teardown():
try: try:
next() next(it)
except StopIteration: except StopIteration:
pass pass
else: else:
fail_fixturefunc(fixturefunc, fail_fixturefunc(fixturefunc,
"yield_fixture function has more than one 'yield'") "yield_fixture function has more than one 'yield'")
request.addfinalizer(teardown) request.addfinalizer(teardown)
else: else:
if is_generator(fixturefunc):
fail_fixturefunc(fixturefunc,
msg="pytest.fixture functions cannot use ``yield``. "
"Instead write and return an inner function/generator "
"and let the consumer call and iterate over it.")
res = fixturefunc(**kwargs) res = fixturefunc(**kwargs)
return res return res
class FixtureDef: class FixtureDef:
""" A container for a factory definition. """ """ A container for a factory definition. """
def __init__(self, fixturemanager, baseid, argname, func, scope, params, def __init__(self, fixturemanager, baseid, argname, func, scope, params,
yieldctx, unittest=False, ids=None): unittest=False, ids=None):
self._fixturemanager = fixturemanager self._fixturemanager = fixturemanager
self.baseid = baseid or '' self.baseid = baseid or ''
self.has_location = baseid is not None self.has_location = baseid is not None
@ -2367,7 +2357,6 @@ class FixtureDef:
self.params = params self.params = params
startindex = unittest and 1 or None startindex = unittest and 1 or None
self.argnames = getfuncargnames(func, startindex=startindex) self.argnames = getfuncargnames(func, startindex=startindex)
self.yieldctx = yieldctx
self.unittest = unittest self.unittest = unittest
self.ids = ids self.ids = ids
self._finalizer = [] self._finalizer = []
@ -2428,8 +2417,7 @@ class FixtureDef:
fixturefunc = fixturefunc.__get__(request.instance) fixturefunc = fixturefunc.__get__(request.instance)
try: try:
result = call_fixture_func(fixturefunc, request, kwargs, result = call_fixture_func(fixturefunc, request, kwargs)
self.yieldctx)
except Exception: except Exception:
self.cached_result = (None, my_cache_key, sys.exc_info()) self.cached_result = (None, my_cache_key, sys.exc_info())
raise raise

View File

@ -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):

View File

@ -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::

View File

@ -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::

View File

@ -1,100 +1,17 @@
.. _yieldfixture: .. _yieldfixture:
Fixture functions using "yield" / context manager integration "yield_fixture" functions
--------------------------------------------------------------- ---------------------------------------------------------------
.. deprecated:: 2.10
.. versionadded:: 2.4 .. versionadded:: 2.4
.. regendoc:wipe .. 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``
in previous versions.
pytest-2.4 allows fixture functions to seamlessly use a ``yield`` instead Marking functions as ``yield_fixture`` is still supported, but deprecated and should not
of a ``return`` statement to provide a fixture value while otherwise be used in new code.
fully supporting all other fixture features.
Let's look at a simple standalone-example using the ``yield`` syntax::
# content of test_yield.py
import pytest
@pytest.yield_fixture
def passwd():
print ("\nsetup before yield")
f = open("/etc/passwd")
yield f.readlines()
print ("teardown after yield")
f.close()
def test_has_lines(passwd):
print ("test called")
assert passwd
In contrast to :ref:`finalization through registering callbacks
<finalization>`, our fixture function used a ``yield``
statement to provide the lines of the ``/etc/passwd`` file.
The code after the ``yield`` statement serves as the teardown code,
avoiding the indirection of registering a teardown callback function.
Let's run it with output capturing disabled::
$ py.test -q -s test_yield.py
setup before yield
test called
.teardown after yield
1 passed in 0.12 seconds
We can also seamlessly use the new syntax with ``with`` statements.
Let's simplify the above ``passwd`` fixture::
# content of test_yield2.py
import pytest
@pytest.yield_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 that the yield fixture form supports all other fixture
features such as ``scope``, ``params``, etc., thus changing existing
fixture functions to use ``yield`` is straightforward.
.. note::
While the ``yield`` syntax is similar to what
:py:func:`contextlib.contextmanager` decorated functions
provide, with pytest fixture functions the part after the
"yield" will always be invoked, independently from the
exception status of the test function which uses the fixture.
This behaviour makes sense if you consider that many different
test functions might use a module or session scoped fixture.
Discussion and future considerations / feedback
++++++++++++++++++++++++++++++++++++++++++++++++++++
There are some topics that are worth mentioning:
- usually ``yield`` is used for producing multiple values.
But fixture functions can only yield exactly one value.
Yielding a second fixture value will get you an error.
It's possible we can evolve pytest to allow for producing
multiple values as an alternative to current parametrization.
For now, you can just use the normal
:ref:`fixture parametrization <fixture-parametrize>`
mechanisms together with ``yield``-style fixtures.
- lastly ``yield`` introduces more than one way to write
fixture functions, so what's the obvious way to a newcomer?
If you want to feedback or participate in discussion of the above
topics, please join our :ref:`contact channels`, you are most welcome.

View File

@ -2597,11 +2597,13 @@ class TestShowFixtures:
''') ''')
@pytest.mark.parametrize('flavor', ['fixture', 'yield_fixture'])
class TestContextManagerFixtureFuncs: class TestContextManagerFixtureFuncs:
def test_simple(self, testdir):
def test_simple(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture @pytest.{flavor}
def arg1(): def arg1():
print ("setup") print ("setup")
yield 1 yield 1
@ -2611,7 +2613,7 @@ class TestContextManagerFixtureFuncs:
def test_2(arg1): def test_2(arg1):
print ("test2 %s" % arg1) print ("test2 %s" % arg1)
assert 0 assert 0
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
*setup* *setup*
@ -2622,10 +2624,10 @@ class TestContextManagerFixtureFuncs:
*teardown* *teardown*
""") """)
def test_scoped(self, testdir): def test_scoped(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture(scope="module") @pytest.{flavor}(scope="module")
def arg1(): def arg1():
print ("setup") print ("setup")
yield 1 yield 1
@ -2634,7 +2636,7 @@ class TestContextManagerFixtureFuncs:
print ("test1 %s" % arg1) print ("test1 %s" % arg1)
def test_2(arg1): def test_2(arg1):
print ("test2 %s" % arg1) print ("test2 %s" % arg1)
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
*setup* *setup*
@ -2643,94 +2645,62 @@ class TestContextManagerFixtureFuncs:
*teardown* *teardown*
""") """)
def test_setup_exception(self, testdir): def test_setup_exception(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture(scope="module") @pytest.{flavor}(scope="module")
def arg1(): def arg1():
pytest.fail("setup") pytest.fail("setup")
yield 1 yield 1
def test_1(arg1): def test_1(arg1):
pass pass
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
*pytest.fail*setup* *pytest.fail*setup*
*1 error* *1 error*
""") """)
def test_teardown_exception(self, testdir): def test_teardown_exception(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture(scope="module") @pytest.{flavor}(scope="module")
def arg1(): def arg1():
yield 1 yield 1
pytest.fail("teardown") pytest.fail("teardown")
def test_1(arg1): def test_1(arg1):
pass pass
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
*pytest.fail*teardown* *pytest.fail*teardown*
*1 passed*1 error* *1 passed*1 error*
""") """)
def test_yields_more_than_one(self, testdir): def test_yields_more_than_one(self, testdir, flavor):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture(scope="module") @pytest.{flavor}(scope="module")
def arg1(): def arg1():
yield 1 yield 1
yield 2 yield 2
def test_1(arg1): def test_1(arg1):
pass pass
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines(""" result.stdout.fnmatch_lines("""
*fixture function* *fixture function*
*test_yields*:2* *test_yields*:2*
""") """)
def test_custom_name(self, testdir, flavor):
def test_no_yield(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import pytest import pytest
@pytest.yield_fixture(scope="module") @pytest.{flavor}(name='meow')
def arg1():
return 1
def test_1(arg1):
pass
""")
result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("""
*yield_fixture*requires*yield*
*yield_fixture*
*def arg1*
""")
def test_yield_not_allowed_in_non_yield(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(scope="module")
def arg1():
yield 1
def test_1(arg1):
pass
""")
result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("""
*fixture*cannot use*yield*
*def arg1*
""")
def test_custom_name(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(name='meow')
def arg1(): def arg1():
return 'mew' return 'mew'
def test_1(meow): def test_1(meow):
print(meow) print(meow)
""") """.format(flavor=flavor))
result = testdir.runpytest("-s") result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("*mew*") result.stdout.fnmatch_lines("*mew*")