Skip to content

Commit

Permalink
Allow parameterised fixtures to give paramemter IDs
Browse files Browse the repository at this point in the history
This is just like the markers etc already can do.
  • Loading branch information
flub committed Dec 15, 2013
1 parent 0182697 commit 46fc29b
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Unreleased
-----------------------------------

- Allow parameterized fixtures to specify the ID of the parameters by
adding an ids argument to pytest.fixture() and pytest.yield_fixture().

- fix issue404 by always using the binary xml escape in the junitxml plugin

2.5.0
Expand Down
25 changes: 17 additions & 8 deletions _pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ def getimfunc(func):


class FixtureFunctionMarker:
def __init__(self, scope, params, autouse=False, yieldctx=False):
def __init__(self, scope, params,
autouse=False, yieldctx=False, ids=None):
self.scope = scope
self.params = params
self.autouse = autouse
self.yieldctx = yieldctx
self.ids = ids

def __call__(self, function):
if inspect.isclass(function):
Expand All @@ -49,7 +51,7 @@ def __call__(self, function):
return function


def fixture(scope="function", params=None, autouse=False):
def fixture(scope="function", params=None, autouse=False, ids=None):
""" (return a) decorator to mark a fixture factory function.
This decorator can be used (with or or without parameters) to define
Expand All @@ -71,16 +73,20 @@ def fixture(scope="function", params=None, autouse=False):
can see it. If False (the default) then an explicit
reference is needed to activate the fixture.
:arg ids: list of string ids each corresponding to the argvalues
so that they are part of the test id. If no ids are provided
they will be generated automatically from the argvalues.
"""
if callable(scope) and params is None and autouse == False:
# direct decoration
return FixtureFunctionMarker(
"function", params, autouse)(scope)
if params is not None and not isinstance(params, (list, tuple)):
params = list(params)
return FixtureFunctionMarker(scope, params, autouse)
return FixtureFunctionMarker(scope, params, autouse, ids=ids)

def yield_fixture(scope="function", params=None, autouse=False):
def yield_fixture(scope="function", params=None, autouse=False, ids=None):
""" (return a) decorator to mark a yield-fixture factory function
(EXPERIMENTAL).
Expand All @@ -94,7 +100,8 @@ def yield_fixture(scope="function", params=None, autouse=False):
return FixtureFunctionMarker(
"function", params, autouse, yieldctx=True)(scope)
else:
return FixtureFunctionMarker(scope, params, autouse, yieldctx=True)
return FixtureFunctionMarker(scope, params, autouse,
yieldctx=True, ids=ids)

defaultfuncargprefixmarker = fixture()

Expand Down Expand Up @@ -1623,7 +1630,8 @@ def pytest_generate_tests(self, metafunc):
for fixturedef in faclist:
if fixturedef.params is not None:
metafunc.parametrize(argname, fixturedef.params,
indirect=True, scope=fixturedef.scope)
indirect=True, scope=fixturedef.scope,
ids=fixturedef.ids)

def pytest_collection_modifyitems(self, items):
# separate parametrized setups
Expand Down Expand Up @@ -1660,7 +1668,7 @@ def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
fixturedef = FixtureDef(self, nodeid, name, obj,
marker.scope, marker.params,
yieldctx=marker.yieldctx,
unittest=unittest)
unittest=unittest, ids=marker.ids)
faclist = self._arg2fixturedefs.setdefault(name, [])
if fixturedef.has_location:
faclist.append(fixturedef)
Expand Down Expand Up @@ -1728,7 +1736,7 @@ def teardown():
class FixtureDef:
""" A container for a factory definition. """
def __init__(self, fixturemanager, baseid, argname, func, scope, params,
yieldctx, unittest=False):
yieldctx, unittest=False, ids=None):
self._fixturemanager = fixturemanager
self.baseid = baseid or ''
self.has_location = baseid is not None
Expand All @@ -1741,6 +1749,7 @@ def __init__(self, fixturemanager, baseid, argname, func, scope, params,
self.argnames = getfuncargnames(func, startindex=startindex)
self.yieldctx = yieldctx
self.unittest = unittest
self.ids = ids
self._finalizer = []

def addfinalizer(self, finalizer):
Expand Down
34 changes: 34 additions & 0 deletions testing/python/fixture.py
Original file line number Diff line number Diff line change
Expand Up @@ -1989,6 +1989,40 @@ def test_something(test_app):
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)

def test_params_and_ids(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.fixture(params=[object(), object()],
ids=['alpha', 'beta'])
def fix(request):
return request.param
def test_foo(fix):
assert 1
""")
res = testdir.runpytest('-v')
res.stdout.fnmatch_lines([
'*test_foo*alpha*',
'*test_foo*beta*'])

def test_params_and_ids_yieldfixture(self, testdir):
testdir.makepyfile("""
import pytest
@pytest.yield_fixture(params=[object(), object()],
ids=['alpha', 'beta'])
def fix(request):
yield request.param
def test_foo(fix):
assert 1
""")
res = testdir.runpytest('-v')
res.stdout.fnmatch_lines([
'*test_foo*alpha*',
'*test_foo*beta*'])


class TestRequestScopeAccess:
pytestmark = pytest.mark.parametrize(("scope", "ok", "error"),[
Expand Down

0 comments on commit 46fc29b

Please sign in to comment.