Allow custom fixture names for fixtures

When defining a fixture in the same module as where it is used, the
function argument shadows the fixture name, which a) annoys pylint and
b) can lead to bugs where you forget to request a fixture into a test
method.

This allows one to define fixtures with a different name than the name
of the function, bypassing that problem.
This commit is contained in:
Mike Lundy 2016-03-08 18:16:57 -08:00
parent c2b9196a7c
commit 9577120592
4 changed files with 33 additions and 5 deletions

View File

@ -63,6 +63,7 @@ Matt Williams
Michael Aquilina
Michael Birtwell
Michael Droettboom
Mike Lundy
Nicolas Delaby
Pieter Mulder
Piotr Banaszkiewicz

View File

@ -7,7 +7,11 @@
namespace in which your doctests run.
Thanks `@milliams`_ for the complete PR (`#1428`_).
*
* New ``name`` argument to ``pytest.fixture`` mark, which allows a custom name
for a fixture (to solve the funcarg-shadowing-fixture problem).
Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
*
*
@ -21,8 +25,10 @@
*
.. _@milliams: https://github.com/milliams
.. _@novas0x2a: https://github.com/novas0x2a
.. _#1428: https://github.com/pytest-dev/pytest/pull/1428
.. _#1444: https://github.com/pytest-dev/pytest/pull/1444
2.9.1.dev1

View File

@ -114,12 +114,13 @@ def safe_getattr(object, name, default):
class FixtureFunctionMarker:
def __init__(self, scope, params,
autouse=False, yieldctx=False, ids=None):
autouse=False, yieldctx=False, ids=None, name=None):
self.scope = scope
self.params = params
self.autouse = autouse
self.yieldctx = yieldctx
self.ids = ids
self.name = name
def __call__(self, function):
if isclass(function):
@ -129,7 +130,7 @@ class FixtureFunctionMarker:
return function
def fixture(scope="function", params=None, autouse=False, ids=None):
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
""" (return a) decorator to mark a fixture factory function.
This decorator can be used (with or or without parameters) to define
@ -155,14 +156,21 @@ def fixture(scope="function", params=None, autouse=False, ids=None):
so that they are part of the test id. If no ids are provided
they will be generated automatically from the params.
:arg name: the name of the fixture. This defaults to the name of the
decorated function. If a fixture is used in the same module in
which it is defined, the function name of the fixture will be
shadowed by the function arg that requests the fixture; one way
to resolve this is to name the decorated function
``fixture_<fixturename>`` and then use
``@pytest.fixture(name='<fixturename>')``.
"""
if callable(scope) and params is None and autouse == False:
# direct decoration
return FixtureFunctionMarker(
"function", params, autouse)(scope)
"function", params, autouse, name=name)(scope)
if params is not None and not isinstance(params, (list, tuple)):
params = list(params)
return FixtureFunctionMarker(scope, params, autouse, ids=ids)
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
@ -1989,6 +1997,8 @@ class FixtureManager:
# fixture attribute
continue
else:
if marker.name:
name = marker.name
assert not name.startswith(self._argprefix)
fixturedef = FixtureDef(self, nodeid, name, obj,
marker.scope, marker.params,

View File

@ -2691,3 +2691,14 @@ class TestContextManagerFixtureFuncs:
*def arg1*
""")
def test_custom_name(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(name='meow')
def arg1():
return 'mew'
def test_1(meow):
print(meow)
""")
result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("*mew*")