Skip to content

Pytest does not support indirect parameters for overriden fixtures #2043

@acatton

Description

@acatton

Here's my minimum test case:

$ pip freeze
py==1.4.31
pytest==3.0.3

Here's my test case:

import pytest


@pytest.fixture(params=[3])
def a(request):
    return 'a' * request.param


@pytest.fixture(params=[2])
def b(request):
    return 'b' * request.param


@pytest.fixture
def ab(a, b):
    return ':'.join([a, b])


class OverridenMixin(object):
    @pytest.fixture
    def ab(self, ab):
        """Just override 'ab' in this class"""
        return '--{}--'.format(ab)


class TestNormal(object):
    def test_ab(self, ab):
        assert ab == 'aaa:bb'

    @pytest.mark.parametrize('a,b', [(1, 1)], indirect=True)
    def test_ab_with_different_parameters(self, ab):
        assert ab == 'a:b'


class TestOverriden(OverridenMixin):
    def test_ab(self, ab):
        assert ab == '--aaa:bb--'

    @pytest.mark.parametrize('a,b', [(1, 1)], indirect=True)
    def test_ab_with_different_parameters(self, ab):
        assert ab == '--a:b--'

I get the error:

=============================================================================================== test session starts ===============================================================================================
platform linux2 -- Python 2.7.12, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- /tmp/tmp.5aQF8ckzpV/.tmp.5aQF8ckzpV/bin/python
cachedir: .cache
rootdir: /tmp/tmp.5aQF8ckzpV, inifile: 
collected 2 items / 1 errors 

===================================================================================================== ERRORS ======================================================================================================
______________________________________________________________________________________________ ERROR collecting a.py ______________________________________________________________________________________________
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/runner.py:163: in __init__
    self.result = func()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/main.py:460: in _memocollect
    return self._memoizedcall('_collected', lambda: list(self.collect()))
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/main.py:331: in _memoizedcall
    res = function()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/main.py:460: in <lambda>
    return self._memoizedcall('_collected', lambda: list(self.collect()))
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:521: in collect
    return super(Instance, self).collect()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:318: in collect
    res = self.makeitem(name, obj)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:330: in makeitem
    collector=self, name=name, obj=obj)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:745: in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:339: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:334: in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:613: in execute
    return _wrapped_call(hook_impl.function(*args), self.execute)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:250: in _wrapped_call
    wrap_controller.send(call_outcome)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:192: in pytest_pycollect_makeitem
    res = list(collector._genfunctions(name, obj))
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:350: in _genfunctions
    self.ihook.pytest_generate_tests(metafunc=metafunc)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:745: in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:339: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:334: in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:614: in execute
    res = hook_impl.function(*args)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:105: in pytest_generate_tests
    metafunc.parametrize(*marker.args, **marker.kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:813: in parametrize
    self.function, name, arg))
E   ValueError: <function test_ab_with_different_parameters at 0x7fc6ecdb4de8> uses no fixture 'a'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================================= 1 error in 0.41 seconds =============================================================================================

More interesting, if I commented out the last test (TestOverriden::test_ab_with_different_parameters) I get:

===================================================================================================== ERRORS ======================================================================================================
_____________________________________________________________________________________ ERROR at setup of TestOverriden.test_ab _____________________________________________________________________________________

self = <CallInfo when='setup' exception: The requested fixture has no parameter defined for the current test.

Requested fixture 'a' defined in:
a.py:5

Requested here:
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:529>
func = <function <lambda> at 0x7f726a6a6e60>, when = 'setup'

    def __init__(self, func, when):
        #: context of invocation: one of "setup", "call",
        #: "teardown", "memocollect"
        self.when = when
        self.start = time()
        try:
>           self.result = func()

.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/runner.py:163: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/runner.py:151: in <lambda>
    return CallInfo(lambda: ihook(item=item, **kwds), when=when)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:745: in __call__
    return self._hookexec(self, self._nonwrappers + self._wrappers, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:339: in _hookexec
    return self._inner_hookexec(hook, methods, kwargs)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:334: in <lambda>
    _MultiCall(methods, kwargs, hook.spec_opts).execute()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:613: in execute
    return _wrapped_call(hook_impl.function(*args), self.execute)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:254: in _wrapped_call
    return call_outcome.get_result()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:280: in get_result
    _reraise(*ex)  # noqa
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:265: in __init__
    self.result = func()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/vendored_packages/pluggy.py:614: in execute
    res = hook_impl.function(*args)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/runner.py:100: in pytest_runtest_setup
    item.session._setupstate.prepare(item)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/runner.py:453: in prepare
    col.setup()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/python.py:1560: in setup
    fixtures.fillfixtures(self)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:252: in fillfixtures
    request._fillfixtures()
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:397: in _fillfixtures
    item.funcargs[argname] = self.getfixturevalue(argname)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:439: in getfixturevalue
    return self._get_active_fixturedef(argname).cached_result[0]
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:464: in _get_active_fixturedef
    result = self._getfixturevalue(fixturedef)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:529: in _getfixturevalue
    val = fixturedef.execute(request=subrequest)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:766: in execute
    fixturedef = request._get_active_fixturedef(argname)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:464: in _get_active_fixturedef
    result = self._getfixturevalue(fixturedef)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:529: in _getfixturevalue
    val = fixturedef.execute(request=subrequest)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:766: in execute
    fixturedef = request._get_active_fixturedef(argname)
.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:464: in _get_active_fixturedef
    result = self._getfixturevalue(fixturedef)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <SubRequest 'ab' for <Function 'test_ab'>>, fixturedef = <FixtureDef name='a' scope='function' baseid='a.py' >

    def _getfixturevalue(self, fixturedef):
        # prepare a subrequest object before calling fixture function
        # (latter managed by fixturedef)
        argname = fixturedef.argname
        funcitem = self._pyfuncitem
        scope = fixturedef.scope
        try:
            param = funcitem.callspec.getparam(argname)
        except (AttributeError, ValueError):
            param = NOTSET
            param_index = 0
            if fixturedef.params is not None:
                frame = inspect.stack()[3]
                frameinfo = inspect.getframeinfo(frame[0])
                source_path = frameinfo.filename
                source_lineno = frameinfo.lineno
                source_path = py.path.local(source_path)
                if source_path.relto(funcitem.config.rootdir):
                    source_path = source_path.relto(funcitem.config.rootdir)
                msg = (
                    "The requested fixture has no parameter defined for the "
                    "current test.\n\nRequested fixture '{0}' defined in:\n{1}"
                    "\n\nRequested here:\n{2}:{3}".format(
                        fixturedef.argname,
                        getlocation(fixturedef.func, funcitem.config.rootdir),
                        source_path,
                        source_lineno,
                    )
                )
>               pytest.fail(msg)
E               Failed: The requested fixture has no parameter defined for the current test.
E               
E               Requested fixture 'a' defined in:
E               a.py:5
E               
E               Requested here:
E               .tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:529

.tmp.5aQF8ckzpV/lib/python2.7/site-packages/_pytest/fixtures.py:509: Failed
======================================================================================== 2 passed, 1 error in 0.39 seconds ========================================================================================

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: fixturesanything involving fixtures directly or indirectlytype: refactoringinternal improvements to the code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions